| 1 | /***************************************************************************** |
| 2 | |
| 3 | Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. |
| 4 | Copyright (c) 2018, 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, Suite 500, Boston, MA 02110-1335 USA |
| 17 | |
| 18 | *****************************************************************************/ |
| 19 | |
| 20 | /**************************************************//** |
| 21 | @file include/read0types.h |
| 22 | Cursor read |
| 23 | |
| 24 | Created 2/16/1997 Heikki Tuuri |
| 25 | *******************************************************/ |
| 26 | |
| 27 | #ifndef read0types_h |
| 28 | #define read0types_h |
| 29 | |
| 30 | #include <algorithm> |
| 31 | #include "dict0mem.h" |
| 32 | |
| 33 | #include "trx0types.h" |
| 34 | |
| 35 | |
| 36 | /** View is not visible to purge thread. */ |
| 37 | #define READ_VIEW_STATE_CLOSED 0 |
| 38 | |
| 39 | /** View is being opened, purge thread must wait for state change. */ |
| 40 | #define READ_VIEW_STATE_SNAPSHOT 1 |
| 41 | |
| 42 | /** View is visible to purge thread. */ |
| 43 | #define READ_VIEW_STATE_OPEN 2 |
| 44 | |
| 45 | |
| 46 | /** |
| 47 | Read view lists the trx ids of those transactions for which a consistent read |
| 48 | should not see the modifications to the database. |
| 49 | */ |
| 50 | class ReadView |
| 51 | { |
| 52 | /** |
| 53 | View state. |
| 54 | |
| 55 | It is not defined as enum as it has to be updated using atomic operations. |
| 56 | Possible values are READ_VIEW_STATE_CLOSED, READ_VIEW_STATE_SNAPSHOT and |
| 57 | READ_VIEW_STATE_OPEN. |
| 58 | |
| 59 | Possible state transfers... |
| 60 | |
| 61 | Start view open: |
| 62 | READ_VIEW_STATE_CLOSED -> READ_VIEW_STATE_SNAPSHOT |
| 63 | |
| 64 | Complete view open: |
| 65 | READ_VIEW_STATE_SNAPSHOT -> READ_VIEW_STATE_OPEN |
| 66 | |
| 67 | Close view: |
| 68 | READ_VIEW_STATE_OPEN -> READ_VIEW_STATE_CLOSED |
| 69 | */ |
| 70 | int32_t m_state; |
| 71 | |
| 72 | |
| 73 | public: |
| 74 | ReadView(): m_state(READ_VIEW_STATE_CLOSED), m_low_limit_id(0) {} |
| 75 | |
| 76 | |
| 77 | /** |
| 78 | Copy state from another view. |
| 79 | |
| 80 | This method is used to find min(m_low_limit_no), min(m_low_limit_id) and |
| 81 | all transaction ids below min(m_low_limit_id). These values effectively |
| 82 | form oldest view. |
| 83 | |
| 84 | @param other view to copy from |
| 85 | */ |
| 86 | void copy(const ReadView &other) |
| 87 | { |
| 88 | ut_ad(&other != this); |
| 89 | if (m_low_limit_no > other.m_low_limit_no) |
| 90 | m_low_limit_no= other.m_low_limit_no; |
| 91 | if (m_low_limit_id > other.m_low_limit_id) |
| 92 | m_low_limit_id= other.m_low_limit_id; |
| 93 | |
| 94 | trx_ids_t::iterator dst= m_ids.begin(); |
| 95 | for (trx_ids_t::const_iterator src= other.m_ids.begin(); |
| 96 | src != other.m_ids.end(); src++) |
| 97 | { |
| 98 | if (*src >= m_low_limit_id) |
| 99 | break; |
| 100 | loop: |
| 101 | if (dst == m_ids.end()) |
| 102 | { |
| 103 | m_ids.push_back(*src); |
| 104 | dst= m_ids.end(); |
| 105 | continue; |
| 106 | } |
| 107 | if (*dst < *src) |
| 108 | { |
| 109 | dst++; |
| 110 | goto loop; |
| 111 | } |
| 112 | else if (*dst > *src) |
| 113 | dst= m_ids.insert(dst, *src) + 1; |
| 114 | } |
| 115 | m_ids.erase(std::lower_bound(dst, m_ids.end(), m_low_limit_id), |
| 116 | m_ids.end()); |
| 117 | |
| 118 | m_up_limit_id= m_ids.empty() ? m_low_limit_id : m_ids.front(); |
| 119 | ut_ad(m_up_limit_id <= m_low_limit_id); |
| 120 | } |
| 121 | |
| 122 | |
| 123 | /** |
| 124 | Opens a read view where exactly the transactions serialized before this |
| 125 | point in time are seen in the view. |
| 126 | |
| 127 | View becomes visible to purge thread. |
| 128 | |
| 129 | @param[in,out] trx transaction |
| 130 | */ |
| 131 | void open(trx_t *trx); |
| 132 | |
| 133 | |
| 134 | /** |
| 135 | Closes the view. |
| 136 | |
| 137 | View becomes not visible to purge thread. |
| 138 | */ |
| 139 | void close() |
| 140 | { |
| 141 | ut_ad(m_state == READ_VIEW_STATE_CLOSED || |
| 142 | m_state == READ_VIEW_STATE_OPEN); |
| 143 | if (m_state == READ_VIEW_STATE_OPEN) |
| 144 | my_atomic_store32_explicit(&m_state, READ_VIEW_STATE_CLOSED, |
| 145 | MY_MEMORY_ORDER_RELAXED); |
| 146 | } |
| 147 | |
| 148 | |
| 149 | /** m_state getter for trx_sys::clone_oldest_view() trx_sys::size(). */ |
| 150 | int32_t get_state() const |
| 151 | { |
| 152 | return my_atomic_load32_explicit(const_cast<int32*>(&m_state), |
| 153 | MY_MEMORY_ORDER_ACQUIRE); |
| 154 | } |
| 155 | |
| 156 | |
| 157 | /** |
| 158 | Returns true if view is open. |
| 159 | |
| 160 | Only used by view owner thread, thus we can omit atomic operations. |
| 161 | */ |
| 162 | bool is_open() const |
| 163 | { |
| 164 | ut_ad(m_state == READ_VIEW_STATE_OPEN || |
| 165 | m_state == READ_VIEW_STATE_CLOSED); |
| 166 | return m_state == READ_VIEW_STATE_OPEN; |
| 167 | } |
| 168 | |
| 169 | |
| 170 | /** |
| 171 | Creates a snapshot where exactly the transactions serialized before this |
| 172 | point in time are seen in the view. |
| 173 | |
| 174 | @param[in,out] trx transaction |
| 175 | */ |
| 176 | inline void snapshot(trx_t *trx); |
| 177 | |
| 178 | |
| 179 | /** |
| 180 | Sets the creator transaction id. |
| 181 | |
| 182 | This should be set only for views created by RW transactions. |
| 183 | */ |
| 184 | void set_creator_trx_id(trx_id_t id) |
| 185 | { |
| 186 | ut_ad(id > 0); |
| 187 | ut_ad(m_creator_trx_id == 0); |
| 188 | m_creator_trx_id= id; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /** Check whether transaction id is valid. |
| 193 | @param[in] id transaction id to check |
| 194 | @param[in] name table name */ |
| 195 | static void check_trx_id_sanity( |
| 196 | trx_id_t id, |
| 197 | const table_name_t& name); |
| 198 | |
| 199 | /** Check whether the changes by id are visible. |
| 200 | @param[in] id transaction id to check against the view |
| 201 | @param[in] name table name |
| 202 | @return whether the view sees the modifications of id. */ |
| 203 | bool changes_visible( |
| 204 | trx_id_t id, |
| 205 | const table_name_t& name) const |
| 206 | MY_ATTRIBUTE((warn_unused_result)) |
| 207 | { |
| 208 | if (id < m_up_limit_id || id == m_creator_trx_id) { |
| 209 | |
| 210 | return(true); |
| 211 | } |
| 212 | |
| 213 | check_trx_id_sanity(id, name); |
| 214 | |
| 215 | if (id >= m_low_limit_id) { |
| 216 | |
| 217 | return(false); |
| 218 | |
| 219 | } else if (m_ids.empty()) { |
| 220 | |
| 221 | return(true); |
| 222 | } |
| 223 | |
| 224 | return(!std::binary_search(m_ids.begin(), m_ids.end(), id)); |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | @param id transaction to check |
| 229 | @return true if view sees transaction id */ |
| 230 | bool sees(trx_id_t id) const |
| 231 | { |
| 232 | return(id < m_up_limit_id); |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | Write the limits to the file. |
| 237 | @param file file to write to */ |
| 238 | void print_limits(FILE* file) const |
| 239 | { |
| 240 | fprintf(file, |
| 241 | "Trx read view will not see trx with" |
| 242 | " id >= " TRX_ID_FMT ", sees < " TRX_ID_FMT "\n" , |
| 243 | m_low_limit_id, m_up_limit_id); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | @return the low limit no */ |
| 248 | trx_id_t low_limit_no() const |
| 249 | { |
| 250 | return(m_low_limit_no); |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | @return the low limit id */ |
| 255 | trx_id_t low_limit_id() const |
| 256 | { |
| 257 | return(m_low_limit_id); |
| 258 | } |
| 259 | |
| 260 | |
| 261 | private: |
| 262 | /** The read should not see any transaction with trx id >= this |
| 263 | value. In other words, this is the "high water mark". */ |
| 264 | trx_id_t m_low_limit_id; |
| 265 | |
| 266 | /** The read should see all trx ids which are strictly |
| 267 | smaller (<) than this value. In other words, this is the |
| 268 | low water mark". */ |
| 269 | trx_id_t m_up_limit_id; |
| 270 | |
| 271 | /** trx id of creating transaction, set to TRX_ID_MAX for free |
| 272 | views. */ |
| 273 | trx_id_t m_creator_trx_id; |
| 274 | |
| 275 | /** Set of RW transactions that was active when this snapshot |
| 276 | was taken */ |
| 277 | trx_ids_t m_ids; |
| 278 | |
| 279 | /** The view does not need to see the undo logs for transactions |
| 280 | whose transaction number is strictly smaller (<) than this value: |
| 281 | they can be removed in purge if not needed by other views */ |
| 282 | trx_id_t m_low_limit_no; |
| 283 | }; |
| 284 | |
| 285 | #endif |
| 286 | |