1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 #ifndef TRANSACTION_INFO_INCLUDED
24 #define TRANSACTION_INFO_INCLUDED
25 
26 #include "my_global.h"
27 #include "my_dbug.h"                   // DBUG_ENTER
28 #include "my_sys.h"                    // strmake_root
29 #include "xa.h"                        // XID_STATE
30 #include "my_alloc.h"                  // MEM_ROOT
31 #include "thr_malloc.h"                // init_sql_alloc
32 #include "sql_cache.h"                 // query_cache
33 #include "mdl.h"                       // MDL_savepoint
34 #include "handler.h"                   // handlerton
35 #include "rpl_transaction_ctx.h"       // Rpl_transaction_ctx
36 #include "rpl_transaction_write_set_ctx.h" // Transaction_write_set_ctx
37 
38 class THD;
39 
40 typedef struct st_changed_table_list
41 {
42   struct	st_changed_table_list *next;
43   char		*key;
44   uint32        key_length;
45 } CHANGED_TABLE_LIST;
46 
47 
48 /**
49   Either statement transaction or normal transaction - related
50   thread-specific storage engine data.
51 
52   If a storage engine participates in a statement/transaction,
53   an instance of this class is present in
54   thd->m_transaction.m_scope_info[STMT|SESSION].ha_list. The addition
55   this list is made by trans_register_ha().
56 
57   When it's time to commit or rollback, each element of ha_list
58   is used to access storage engine's prepare()/commit()/rollback()
59   methods, and also to evaluate if a full two phase commit is
60   necessary.
61 
62   @sa General description of transaction handling in handler.cc.
63 */
64 
65 class Ha_trx_info
66 {
67 public:
68 
69   /**
70     Register this storage engine in the given transaction context.
71   */
72 
register_ha(Ha_trx_info * ha_info,handlerton * ht_arg)73   void register_ha(Ha_trx_info *ha_info, handlerton *ht_arg)
74   {
75     DBUG_ENTER("Ha_trx_info::register_ha");
76     DBUG_PRINT("enter", ("ht: 0x%llx (%s)",
77                          (ulonglong) ht_arg,
78                          ha_legacy_type_name(ht_arg->db_type)));
79     assert(m_flags == 0);
80     assert(m_ht == NULL);
81     assert(m_next == NULL);
82 
83     m_ht= ht_arg;
84     m_flags= (int) TRX_READ_ONLY; /* Assume read-only at start. */
85 
86     m_next= ha_info;
87 
88     DBUG_VOID_RETURN;
89   }
90 
91   /**
92     Clear, prepare for reuse.
93   */
94 
reset()95   void reset()
96   {
97     DBUG_ENTER("Ha_trx_info::reset");
98     m_next= NULL;
99     m_ht= NULL;
100     m_flags= 0;
101     DBUG_VOID_RETURN;
102   }
103 
Ha_trx_info()104   Ha_trx_info()
105   {
106     reset();
107   }
108 
set_trx_read_write()109   void set_trx_read_write()
110   {
111     assert(is_started());
112     m_flags|= (int) TRX_READ_WRITE;
113   }
114 
is_trx_read_write()115   bool is_trx_read_write() const
116   {
117     assert(is_started());
118     return m_flags & (int) TRX_READ_WRITE;
119   }
120 
121   /**
122     Set the transaction flag to noop_read_write
123     If the transaction has no operation dml statement.
124   */
set_trx_noop_read_write()125   void set_trx_noop_read_write()
126   {
127     assert(is_started());
128     m_flags|= (int) TRX_NOOP_READ_WRITE;
129   }
130 
131   /**
132     Check if the stmt transaction has noop_read_write flag set.
133   */
is_trx_noop_read_write()134   bool is_trx_noop_read_write() const
135   {
136     assert(is_started());
137     return m_flags & (int) TRX_NOOP_READ_WRITE;
138   }
139 
is_started()140   bool is_started() const
141   {
142     return m_ht != NULL;
143   }
144 
145 
146   /**
147     Mark this transaction read-write if the argument is read-write.
148   */
149 
coalesce_trx_with(const Ha_trx_info * stmt_trx)150   void coalesce_trx_with(const Ha_trx_info *stmt_trx)
151   {
152     /*
153       Must be called only after the transaction has been started.
154       Can be called many times, e.g. when we have many
155       read-write statements in a transaction.
156     */
157     assert(is_started());
158     if (stmt_trx->is_trx_read_write())
159       set_trx_read_write();
160     if (stmt_trx->is_trx_noop_read_write())
161       set_trx_noop_read_write();
162   }
163 
next()164   Ha_trx_info *next() const
165   {
166     assert(is_started());
167     return m_next;
168   }
169 
ht()170   handlerton *ht() const
171   {
172     assert(is_started());
173     return m_ht;
174   }
175 
176 private:
177   enum { TRX_READ_ONLY= 0, TRX_READ_WRITE= 1, TRX_NOOP_READ_WRITE= 2 };
178   /**
179     Auxiliary, used for ha_list management
180   */
181   Ha_trx_info *m_next;
182 
183   /**
184     Although a given Ha_trx_info instance is currently always used
185     for the same storage engine, 'ht' is not-NULL only when the
186     corresponding storage is a part of a transaction.
187   */
188   handlerton *m_ht;
189 
190   /**
191     Transaction flags related to this engine.
192     Not-null only if this instance is a part of transaction.
193     May assume a combination of enum values above.
194   */
195   uchar       m_flags;
196 };
197 
198 struct st_savepoint
199 {
200   struct st_savepoint *prev;
201   char                *name;
202   size_t              length;
203   Ha_trx_info         *ha_list;
204   /** State of metadata locks before this savepoint was set. */
205   MDL_savepoint        mdl_savepoint;
206 };
207 
208 class Transaction_ctx
209 {
210 public:
211   enum enum_trx_scope { STMT= 0, SESSION };
212 
213   SAVEPOINT *m_savepoints;
214 
215 private:
216   struct THD_TRANS
217   {
218     /* true is not all entries in the ht[] support 2pc */
219     bool        m_no_2pc;
220     int         m_rw_ha_count;
221     /* storage engines that registered in this transaction */
222     Ha_trx_info *m_ha_list;
223 
224   private:
225     /*
226       The purpose of this member variable (i.e. flag) is to keep track of
227       statements which cannot be rolled back safely(completely).
228       For example,
229 
230       * statements that modified non-transactional tables. The value
231       MODIFIED_NON_TRANS_TABLE is set within mysql_insert, mysql_update,
232       mysql_delete, etc if a non-transactional table is modified.
233 
234       * 'DROP TEMPORARY TABLE' and 'CREATE TEMPORARY TABLE' statements.
235       The former sets the value DROPPED_TEMP_TABLE and the latter
236       the value CREATED_TEMP_TABLE.
237 
238       The tracked statements are modified in scope of:
239 
240       * transaction, when the variable is a member of
241       THD::m_transaction.m_scope_info[SESSION]
242 
243       * top-level statement or sub-statement, when the variable is a
244       member of THD::m_transaction.m_scope_info[STMT]
245 
246       This member has the following life cycle:
247 
248       * m_scope_info[STMT].m_unsafe_rollback_flags is used to keep track of
249       top-level statements which cannot be rolled back safely. At the end of the
250       statement, the value of m_scope_info[STMT].m_unsafe_rollback_flags is
251       merged with m_scope_info[SESSION].m_unsafe_rollback_flags
252       and gets reset.
253 
254       * m_scope_info[SESSION].cannot_safely_rollback is reset at the end
255       of transaction
256 
257       * Since we do not have a dedicated context for execution of
258       a sub-statement, to keep track of non-transactional changes in a
259       sub-statement, we re-use m_scope_info[STMT].m_unsafe_rollback_flags.
260       At entrance into a sub-statement, a copy of the value of
261       m_scope_info[STMT].m_unsafe_rollback_flags (containing the changes of the
262       outer statement) is saved on stack.
263       Then m_scope_info[STMT].m_unsafe_rollback_flags is reset to 0 and the
264       substatement is executed. Then the new value is merged
265       with the saved value.
266     */
267 
268     unsigned int m_unsafe_rollback_flags;
269     /*
270       Define the type of statements which cannot be rolled back safely.
271       Each type occupies one bit in m_unsafe_rollback_flags.
272     */
273     static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01;
274     static unsigned int const CREATED_TEMP_TABLE= 0x02;
275     static unsigned int const DROPPED_TEMP_TABLE= 0x04;
276 
277   public:
cannot_safely_rollbackTHD_TRANS278     bool cannot_safely_rollback() const
279     {
280       return m_unsafe_rollback_flags > 0;
281     }
get_unsafe_rollback_flagsTHD_TRANS282     unsigned int get_unsafe_rollback_flags() const
283     {
284       return m_unsafe_rollback_flags;
285     }
set_unsafe_rollback_flagsTHD_TRANS286     void set_unsafe_rollback_flags(unsigned int flags)
287     {
288       DBUG_PRINT("debug", ("set_unsafe_rollback_flags: %d", flags));
289       m_unsafe_rollback_flags= flags;
290     }
add_unsafe_rollback_flagsTHD_TRANS291     void add_unsafe_rollback_flags(unsigned int flags)
292     {
293       DBUG_PRINT("debug", ("add_unsafe_rollback_flags: %d", flags));
294       m_unsafe_rollback_flags|= flags;
295     }
reset_unsafe_rollback_flagsTHD_TRANS296     void reset_unsafe_rollback_flags()
297     {
298       DBUG_PRINT("debug", ("reset_unsafe_rollback_flags"));
299       m_unsafe_rollback_flags= 0;
300     }
mark_modified_non_trans_tableTHD_TRANS301     void mark_modified_non_trans_table()
302     {
303       DBUG_PRINT("debug", ("mark_modified_non_trans_table"));
304       m_unsafe_rollback_flags|= MODIFIED_NON_TRANS_TABLE;
305     }
has_modified_non_trans_tableTHD_TRANS306     bool has_modified_non_trans_table() const
307     {
308       return m_unsafe_rollback_flags & MODIFIED_NON_TRANS_TABLE;
309     }
mark_created_temp_tableTHD_TRANS310     void mark_created_temp_table()
311     {
312       DBUG_PRINT("debug", ("mark_created_temp_table"));
313       m_unsafe_rollback_flags|= CREATED_TEMP_TABLE;
314     }
has_created_temp_tableTHD_TRANS315     bool has_created_temp_table() const
316     {
317       return m_unsafe_rollback_flags & CREATED_TEMP_TABLE;
318     }
mark_dropped_temp_tableTHD_TRANS319     void mark_dropped_temp_table()
320     {
321       DBUG_PRINT("debug", ("mark_dropped_temp_table"));
322       m_unsafe_rollback_flags|= DROPPED_TEMP_TABLE;
323     }
has_dropped_temp_tableTHD_TRANS324     bool has_dropped_temp_table() const
325     {
326       return m_unsafe_rollback_flags & DROPPED_TEMP_TABLE;
327     }
328 
resetTHD_TRANS329     void reset()
330     {
331       m_no_2pc= false;
332       m_rw_ha_count= 0;
333       reset_unsafe_rollback_flags();
334     }
is_emptyTHD_TRANS335     bool is_empty() const { return m_ha_list == NULL; }
336   };
337 
338   THD_TRANS m_scope_info[2];
339 
340   XID_STATE m_xid_state;
341 
342   /*
343     Tables changed in transaction (that must be invalidated in query cache).
344     List contain only transactional tables, that not invalidated in query
345     cache (instead of full list of changed in transaction tables).
346   */
347   CHANGED_TABLE_LIST* m_changed_tables;
348   MEM_ROOT m_mem_root; // Transaction-life memory allocation pool
349 
350 public:
351   /*
352     (Mostly) binlog-specific fields use while flushing the caches
353     and committing transactions.
354     We don't use bitfield any more in the struct. Modification will
355     be lost when concurrently updating multiple bit fields. It will
356     cause a race condition in a multi-threaded application. And we
357     already caught a race condition case between xid_written and
358     ready_preempt in MYSQL_BIN_LOG::ordered_commit.
359   */
360   struct
361   {
362     bool enabled;                   // see ha_enable_transaction()
363     bool pending;                   // Is the transaction commit pending?
364     bool xid_written;               // The session wrote an XID
365     bool real_commit;               // Is this a "real" commit?
366     bool commit_low;                // see MYSQL_BIN_LOG::ordered_commit
367     bool run_hooks;                 // Call the after_commit hook
368 #ifndef NDEBUG
369     bool ready_preempt;             // internal in MYSQL_BIN_LOG::ordered_commit
370 #endif
371   } m_flags;
372   /* Binlog-specific logical timestamps. */
373   /*
374     Store for the transaction's commit parent sequence_number.
375     The value specifies this transaction dependency with a "parent"
376     transaction.
377     The member is assigned, when the transaction is about to commit
378     in binlog to a value of the last committed transaction's sequence_number.
379     This and last_committed as numbers are kept ever incremented
380     regardless of binary logs being rotated or when transaction
381     is logged in multiple pieces.
382     However the logger to the binary log may convert them
383     according to its specification.
384   */
385   int64 last_committed;
386   /*
387     The transaction's private logical timestamp assigned at the
388     transaction prepare phase. The timestamp enumerates transactions
389     in the binary log. The value is gained through incrementing (stepping) a
390     global clock.
391     Eventually the value is considered to increase max_committed_transaction
392     system clock when the transaction has committed.
393   */
394   int64 sequence_number;
395 
store_commit_parent(int64 last_arg)396   void store_commit_parent(int64 last_arg)
397   {
398     last_committed= last_arg;
399   }
400 
401   Transaction_ctx();
~Transaction_ctx()402   virtual ~Transaction_ctx()
403   {
404     free_root(&m_mem_root, MYF(0));
405   }
406 
cleanup()407   void cleanup()
408   {
409     DBUG_ENTER("Transaction_ctx::cleanup");
410     m_changed_tables= NULL;
411     m_savepoints= NULL;
412     m_xid_state.cleanup();
413     m_rpl_transaction_ctx.cleanup();
414     m_transaction_write_set_ctx.reset_state();
415     free_root(&m_mem_root,MYF(MY_KEEP_PREALLOC));
416     DBUG_VOID_RETURN;
417   }
418 
is_active(enum_trx_scope scope)419   bool is_active(enum_trx_scope scope) const
420   {
421     return m_scope_info[scope].m_ha_list != NULL;
422   }
423 
424   void push_unsafe_rollback_warnings(THD *thd);
425 
merge_unsafe_rollback_flags()426   void merge_unsafe_rollback_flags()
427   {
428     /*
429       Merge m_scope_info[STMT].unsafe_rollback_flags to
430       m_scope_info[SESSION].unsafe_rollback_flags. If the statement
431       cannot be rolled back safely, the transaction including
432       this statement definitely cannot rolled back safely.
433     */
434     m_scope_info[SESSION].add_unsafe_rollback_flags(
435       m_scope_info[STMT].get_unsafe_rollback_flags());
436   }
437 
init_mem_root_defaults(ulong trans_alloc_block_size,ulong trans_prealloc_size)438   void init_mem_root_defaults(ulong trans_alloc_block_size,
439                               ulong trans_prealloc_size)
440   {
441     reset_root_defaults(&m_mem_root,
442                         trans_alloc_block_size,
443                         trans_prealloc_size);
444   }
445 
transaction_memroot()446   MEM_ROOT* transaction_memroot()
447   {
448     return &m_mem_root;
449   }
450 
allocate_memory(unsigned int size)451   void* allocate_memory(unsigned int size)
452   {
453     return alloc_root(&m_mem_root, size);
454   }
455 
claim_memory_ownership()456   void claim_memory_ownership()
457   {
458     claim_root(&m_mem_root);
459   }
460 
free_memory(myf root_alloc_flags)461   void free_memory(myf root_alloc_flags)
462   {
463     free_root(&m_mem_root, root_alloc_flags);
464   }
465 
strmake(const char * str,size_t len)466   char* strmake(const char *str, size_t len)
467   {
468     return strmake_root(&m_mem_root, str, len);
469   }
470 
invalidate_changed_tables_in_cache()471   void invalidate_changed_tables_in_cache()
472   {
473     if (m_changed_tables)
474       query_cache.invalidate(m_changed_tables);
475   }
476 
477   bool add_changed_table(const char *key, long key_length);
478 
ha_trx_info(enum_trx_scope scope)479   Ha_trx_info* ha_trx_info(enum_trx_scope scope)
480   {
481     return m_scope_info[scope].m_ha_list;
482   }
483 
ha_trx_info(enum_trx_scope scope)484   const Ha_trx_info* ha_trx_info(enum_trx_scope scope) const
485   {
486     return m_scope_info[scope].m_ha_list;
487   }
488 
set_ha_trx_info(enum_trx_scope scope,Ha_trx_info * trx_info)489   void set_ha_trx_info(enum_trx_scope scope, Ha_trx_info *trx_info)
490   {
491     m_scope_info[scope].m_ha_list= trx_info;
492   }
493 
xid_state()494   XID_STATE *xid_state()
495   {
496     return &m_xid_state;
497   }
498 
xid_state()499   const XID_STATE *xid_state() const
500   {
501     return &m_xid_state;
502   }
503 
cannot_safely_rollback(enum_trx_scope scope)504   bool cannot_safely_rollback(enum_trx_scope scope) const
505   {
506     return m_scope_info[scope].cannot_safely_rollback();
507   }
508 
get_unsafe_rollback_flags(enum_trx_scope scope)509   unsigned int get_unsafe_rollback_flags(enum_trx_scope scope) const
510   {
511     return m_scope_info[scope].get_unsafe_rollback_flags();
512   }
513 
set_unsafe_rollback_flags(enum_trx_scope scope,unsigned int flags)514   void set_unsafe_rollback_flags(enum_trx_scope scope, unsigned int flags)
515   {
516     m_scope_info[scope].set_unsafe_rollback_flags(flags);
517   }
518 
add_unsafe_rollback_flags(enum_trx_scope scope,unsigned int flags)519   void add_unsafe_rollback_flags(enum_trx_scope scope, unsigned int flags)
520   {
521     m_scope_info[scope].add_unsafe_rollback_flags(flags);
522   }
523 
reset_unsafe_rollback_flags(enum_trx_scope scope)524   void reset_unsafe_rollback_flags(enum_trx_scope scope)
525   {
526     m_scope_info[scope].reset_unsafe_rollback_flags();
527   }
528 
mark_modified_non_trans_table(enum_trx_scope scope)529   void mark_modified_non_trans_table(enum_trx_scope scope)
530   {
531     m_scope_info[scope].mark_modified_non_trans_table();
532   }
533 
has_modified_non_trans_table(enum_trx_scope scope)534   bool has_modified_non_trans_table(enum_trx_scope scope) const
535   {
536     return m_scope_info[scope].has_modified_non_trans_table();
537   }
538 
mark_created_temp_table(enum_trx_scope scope)539   void mark_created_temp_table(enum_trx_scope scope)
540   {
541     m_scope_info[scope].mark_created_temp_table();
542   }
543 
has_created_temp_table(enum_trx_scope scope)544   bool has_created_temp_table(enum_trx_scope scope) const
545   {
546     return m_scope_info[scope].has_created_temp_table();
547   }
548 
mark_dropped_temp_table(enum_trx_scope scope)549   void mark_dropped_temp_table(enum_trx_scope scope)
550   {
551     m_scope_info[scope].mark_dropped_temp_table();
552   }
553 
has_dropped_temp_table(enum_trx_scope scope)554   bool has_dropped_temp_table(enum_trx_scope scope) const
555   {
556     return m_scope_info[scope].has_dropped_temp_table();
557   }
558 
reset(enum_trx_scope scope)559   void reset(enum_trx_scope scope)
560   {
561     m_scope_info[scope].reset();
562   }
563 
is_empty(enum_trx_scope scope)564   bool is_empty(enum_trx_scope scope) const
565   {
566     return m_scope_info[scope].is_empty();
567   }
568 
set_no_2pc(enum_trx_scope scope,bool value)569   void set_no_2pc(enum_trx_scope scope, bool value)
570   {
571     m_scope_info[scope].m_no_2pc= value;
572   }
573 
no_2pc(enum_trx_scope scope)574   bool no_2pc(enum_trx_scope scope) const
575   {
576     return m_scope_info[scope].m_no_2pc;
577   }
578 
rw_ha_count(enum_trx_scope scope)579   int rw_ha_count(enum_trx_scope scope) const
580   {
581     return m_scope_info[scope].m_rw_ha_count;
582   }
583 
set_rw_ha_count(enum_trx_scope scope,int value)584   void set_rw_ha_count(enum_trx_scope scope, int value)
585   {
586     m_scope_info[scope].m_rw_ha_count= value;
587   }
588 
reset_scope(enum_trx_scope scope)589   void reset_scope(enum_trx_scope scope)
590   {
591     m_scope_info[scope].m_ha_list= 0;
592     m_scope_info[scope].m_no_2pc=0;
593     m_scope_info[scope].m_rw_ha_count= 0;
594   }
595 
596 private:
597   CHANGED_TABLE_LIST* changed_table_dup(const char *key, long key_length);
598 
599   /* routings to adding tables to list of changed in transaction tables */
list_include(CHANGED_TABLE_LIST ** prev,CHANGED_TABLE_LIST * curr,CHANGED_TABLE_LIST * new_table)600   bool list_include(CHANGED_TABLE_LIST** prev,
601                     CHANGED_TABLE_LIST* curr,
602                     CHANGED_TABLE_LIST* new_table)
603   {
604     if (new_table)
605     {
606       *prev= new_table;
607       (*prev)->next= curr;
608       return false;
609     }
610     else
611       return true;
612   }
613 
614 public:
get_rpl_transaction_ctx()615   Rpl_transaction_ctx *get_rpl_transaction_ctx()
616   {
617     return &m_rpl_transaction_ctx;
618   }
619 
get_rpl_transaction_ctx()620   const Rpl_transaction_ctx *get_rpl_transaction_ctx() const
621   {
622     return &m_rpl_transaction_ctx;
623   }
624 
get_transaction_write_set_ctx()625   Rpl_transaction_write_set_ctx *get_transaction_write_set_ctx()
626   {
627     return &m_transaction_write_set_ctx;
628   }
629 
get_transaction_write_set_ctx()630   const Rpl_transaction_write_set_ctx *get_transaction_write_set_ctx() const
631   {
632     return &m_transaction_write_set_ctx;
633   }
634 
635 private:
636   Rpl_transaction_ctx m_rpl_transaction_ctx;
637   Rpl_transaction_write_set_ctx m_transaction_write_set_ctx;
638 };
639 
640 #endif
641