1 /***************************************************************************** 2 3 Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. 4 Copyright (c) 2017, 2019, MariaDB Corporation. 5 6 This program is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free Software 8 Foundation; version 2 of the License. 9 10 This program is distributed in the hope that it will be useful, but WITHOUT 11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 12 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along with 15 this program; if not, write to the Free Software Foundation, Inc., 16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA 17 18 *****************************************************************************/ 19 20 /**************************************************//** 21 @file include/row0purge.h 22 Purge obsolete records 23 24 Created 3/14/1997 Heikki Tuuri 25 *******************************************************/ 26 27 #ifndef row0purge_h 28 #define row0purge_h 29 30 #include "que0types.h" 31 #include "btr0types.h" 32 #include "btr0pcur.h" 33 #include "trx0types.h" 34 #include "row0types.h" 35 #include "row0mysql.h" 36 #include "mysqld.h" 37 #include <queue> 38 39 class MDL_ticket; 40 /** Determines if it is possible to remove a secondary index entry. 41 Removal is possible if the secondary index entry does not refer to any 42 not delete marked version of a clustered index record where DB_TRX_ID 43 is newer than the purge view. 44 45 NOTE: This function should only be called by the purge thread, only 46 while holding a latch on the leaf page of the secondary index entry 47 (or keeping the buffer pool watch on the page). It is possible that 48 this function first returns true and then false, if a user transaction 49 inserts a record that the secondary index entry would refer to. 50 However, in that case, the user transaction would also re-insert the 51 secondary index entry after purge has removed it and released the leaf 52 page latch. 53 @param[in,out] node row purge node 54 @param[in] index secondary index 55 @param[in] entry secondary index entry 56 @param[in,out] sec_pcur secondary index cursor or NULL 57 if it is called for purge buffering 58 operation. 59 @param[in,out] sec_mtr mini-transaction which holds 60 secondary index entry or NULL if it is 61 called for purge buffering operation. 62 @param[in] is_tree true=pessimistic purge, 63 false=optimistic (leaf-page only) 64 @return true if the secondary index record can be purged */ 65 bool 66 row_purge_poss_sec( 67 purge_node_t* node, 68 dict_index_t* index, 69 const dtuple_t* entry, 70 btr_pcur_t* sec_pcur=NULL, 71 mtr_t* sec_mtr=NULL, 72 bool is_tree=false); 73 74 /*************************************************************** 75 Does the purge operation for a single undo log record. This is a high-level 76 function used in an SQL execution graph. 77 @return query thread to run next or NULL */ 78 que_thr_t* 79 row_purge_step( 80 /*===========*/ 81 que_thr_t* thr) /*!< in: query thread */ 82 MY_ATTRIBUTE((nonnull, warn_unused_result)); 83 84 /** Info required to purge a record */ 85 struct trx_purge_rec_t 86 { 87 /** Record to purge */ 88 trx_undo_rec_t *undo_rec; 89 /** File pointer to undo record */ 90 roll_ptr_t roll_ptr; 91 }; 92 93 /* Purge node structure */ 94 95 struct purge_node_t{ 96 que_common_t common; /*!< node type: QUE_NODE_PURGE */ 97 /*----------------------*/ 98 /* Local storage for this graph node */ 99 roll_ptr_t roll_ptr;/* roll pointer to undo log record */ 100 101 undo_no_t undo_no;/*!< undo number of the record */ 102 103 ulint rec_type;/*!< undo log record type: TRX_UNDO_INSERT_REC, 104 ... */ 105 private: 106 /** latest unavailable table ID (do not bother looking up again) */ 107 table_id_t unavailable_table_id; 108 /** the latest modification of the table definition identified by 109 unavailable_table_id, or TRX_ID_MAX */ 110 trx_id_t def_trx_id; 111 public: 112 dict_table_t* table; /*!< table where purge is done */ 113 114 ulint cmpl_info;/* compiler analysis info of an update */ 115 116 upd_t* update; /*!< update vector for a clustered index 117 record */ 118 const dtuple_t* ref; /*!< NULL, or row reference to the next row to 119 handle */ 120 dtuple_t* row; /*!< NULL, or a copy (also fields copied to 121 heap) of the indexed fields of the row to 122 handle */ 123 dict_index_t* index; /*!< NULL, or the next index whose record should 124 be handled */ 125 mem_heap_t* heap; /*!< memory heap used as auxiliary storage for 126 row; this must be emptied after a successful 127 purge of a row */ 128 ibool found_clust;/*!< whether the clustered index record 129 determined by ref was found in the clustered 130 index, and we were able to position pcur on 131 it */ 132 btr_pcur_t pcur; /*!< persistent cursor used in searching the 133 clustered index record */ 134 #ifdef UNIV_DEBUG 135 /** whether the operation is in progress */ 136 bool in_progress; 137 #endif 138 trx_id_t trx_id; /*!< trx id for this purging record */ 139 140 /** meta-data lock for the table name */ 141 MDL_ticket* mdl_ticket; 142 143 /** table id of the previous undo log record */ 144 table_id_t last_table_id; 145 146 /** purge thread */ 147 THD* purge_thd; 148 149 /** metadata lock holds for this number of undo log recs */ 150 int mdl_hold_recs; 151 152 /** Undo recs to purge */ 153 std::queue<trx_purge_rec_t> undo_recs; 154 155 /** Constructor */ purge_node_tpurge_node_t156 explicit purge_node_t(que_thr_t* parent) : 157 common(QUE_NODE_PURGE, parent), 158 unavailable_table_id(0), 159 table(NULL), 160 heap(mem_heap_create(256)), 161 #ifdef UNIV_DEBUG 162 in_progress(false), 163 #endif 164 mdl_ticket(NULL), 165 last_table_id(0), 166 purge_thd(NULL), 167 mdl_hold_recs(0) 168 { 169 } 170 171 #ifdef UNIV_DEBUG 172 /***********************************************************//** 173 Validate the persisent cursor. The purge node has two references 174 to the clustered index record - one via the ref member, and the 175 other via the persistent cursor. These two references must match 176 each other if the found_clust flag is set. 177 @return true if the persistent cursor is consistent with 178 the ref member.*/ 179 bool validate_pcur(); 180 #endif 181 182 /** Determine if a table should be skipped in purge. 183 @param[in] table_id table identifier 184 @return whether to skip the table lookup and processing */ is_skippedpurge_node_t185 bool is_skipped(table_id_t id) const 186 { 187 return id == unavailable_table_id && trx_id <= def_trx_id; 188 } 189 190 /** Remember that a table should be skipped in purge. 191 @param[in] id table identifier 192 @param[in] limit last transaction for which to skip */ skippurge_node_t193 void skip(table_id_t id, trx_id_t limit) 194 { 195 DBUG_ASSERT(limit >= trx_id); 196 unavailable_table_id = id; 197 def_trx_id = limit; 198 } 199 200 /** Start processing an undo log record. */ startpurge_node_t201 void start() 202 { 203 ut_ad(in_progress); 204 DBUG_ASSERT(common.type == QUE_NODE_PURGE); 205 206 row= nullptr; 207 ref= nullptr; 208 index= nullptr; 209 update= nullptr; 210 found_clust= FALSE; 211 rec_type= ULINT_UNDEFINED; 212 cmpl_info= ULINT_UNDEFINED; 213 if (!purge_thd) 214 purge_thd= current_thd; 215 } 216 217 218 /** Close the existing table and release the MDL for it. */ close_tablepurge_node_t219 void close_table() 220 { 221 last_table_id= 0; 222 if (!table) 223 { 224 ut_ad(!mdl_ticket); 225 return; 226 } 227 228 innobase_reset_background_thd(purge_thd); 229 dict_table_close(table, false, false, purge_thd, mdl_ticket); 230 table= nullptr; 231 mdl_ticket= nullptr; 232 } 233 234 235 /** Retail mdl for the table id. 236 @param[in] table_id table id to be processed 237 @return true if retain mdl */ retain_mdlpurge_node_t238 bool retain_mdl(table_id_t table_id) 239 { 240 ut_ad(table_id); 241 if (last_table_id == table_id && mdl_hold_recs < 100) 242 { 243 ut_ad(table); 244 mdl_hold_recs++; 245 return true; 246 } 247 248 mdl_hold_recs= 0; 249 close_table(); 250 return false; 251 } 252 253 254 /** Reset the state at end 255 @return the query graph parent */ endpurge_node_t256 que_node_t* end() 257 { 258 DBUG_ASSERT(common.type == QUE_NODE_PURGE); 259 close_table(); 260 ut_ad(undo_recs.empty()); 261 ut_d(in_progress= false); 262 purge_thd= nullptr; 263 mem_heap_empty(heap); 264 return common.parent; 265 } 266 }; 267 268 #endif 269