1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2021, Oracle and/or its affiliates.
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8 
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation.  The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15 
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 GNU General Public License, version 2.0, for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
24 
25 *****************************************************************************/
26 
27 /**************************************************//**
28 @file read/read0read.cc
29 Cursor read
30 
31 Created 2/16/1997 Heikki Tuuri
32 *******************************************************/
33 
34 #include "read0read.h"
35 #include "read0i_s.h"
36 
37 #include "srv0srv.h"
38 #include "trx0sys.h"
39 
40 /*
41 -------------------------------------------------------------------------------
42 FACT A: Cursor read view on a secondary index sees only committed versions
43 -------
44 of the records in the secondary index or those versions of rows created
45 by transaction which created a cursor before cursor was created even
46 if transaction which created the cursor has changed that clustered index page.
47 
48 PROOF: We must show that read goes always to the clustered index record
49 to see that record is visible in the cursor read view. Consider e.g.
50 following table and SQL-clauses:
51 
52 create table t1(a int not null, b int, primary key(a), index(b));
53 insert into t1 values (1,1),(2,2);
54 commit;
55 
56 Now consider that we have a cursor for a query
57 
58 select b from t1 where b >= 1;
59 
60 This query will use secondary key on the table t1. Now after the first fetch
61 on this cursor if we do a update:
62 
63 update t1 set b = 5 where b = 2;
64 
65 Now second fetch of the cursor should not see record (2,5) instead it should
66 see record (2,2).
67 
68 We also should show that if we have delete t1 where b = 5; we still
69 can see record (2,2).
70 
71 When we access a secondary key record maximum transaction id is fetched
72 from this record and this trx_id is compared to up_limit_id in the view.
73 If trx_id in the record is greater or equal than up_limit_id in the view
74 cluster record is accessed.  Because trx_id of the creating
75 transaction is stored when this view was created to the list of
76 trx_ids not seen by this read view previous version of the
77 record is requested to be built. This is build using clustered record.
78 If the secondary key record is delete-marked, its corresponding
79 clustered record can be already be purged only if records
80 trx_id < low_limit_no. Purge can't remove any record deleted by a
81 transaction which was active when cursor was created. But, we still
82 may have a deleted secondary key record but no clustered record. But,
83 this is not a problem because this case is handled in
84 row_sel_get_clust_rec() function which is called
85 whenever we note that this read view does not see trx_id in the
86 record. Thus, we see correct version. Q. E. D.
87 
88 -------------------------------------------------------------------------------
89 FACT B: Cursor read view on a clustered index sees only committed versions
90 -------
91 of the records in the clustered index or those versions of rows created
92 by transaction which created a cursor before cursor was created even
93 if transaction which created the cursor has changed that clustered index page.
94 
95 PROOF:  Consider e.g.following table and SQL-clauses:
96 
97 create table t1(a int not null, b int, primary key(a));
98 insert into t1 values (1),(2);
99 commit;
100 
101 Now consider that we have a cursor for a query
102 
103 select a from t1 where a >= 1;
104 
105 This query will use clustered key on the table t1. Now after the first fetch
106 on this cursor if we do a update:
107 
108 update t1 set a = 5 where a = 2;
109 
110 Now second fetch of the cursor should not see record (5) instead it should
111 see record (2).
112 
113 We also should show that if we have execute delete t1 where a = 5; after
114 the cursor is opened we still can see record (2).
115 
116 When accessing clustered record we always check if this read view sees
117 trx_id stored to clustered record. By default we don't see any changes
118 if record trx_id >= low_limit_id i.e. change was made transaction
119 which started after transaction which created the cursor. If row
120 was changed by the future transaction a previous version of the
121 clustered record is created. Thus we see only committed version in
122 this case. We see all changes made by committed transactions i.e.
123 record trx_id < up_limit_id. In this case we don't need to do anything,
124 we already see correct version of the record. We don't see any changes
125 made by active transaction except creating transaction. We have stored
126 trx_id of creating transaction to list of trx_ids when this view was
127 created. Thus we can easily see if this record was changed by the
128 creating transaction. Because we already have clustered record we can
129 access roll_ptr. Using this roll_ptr we can fetch undo record.
130 We can now check that undo_no of the undo record is less than undo_no of the
131 trancaction which created a view when cursor was created. We see this
132 clustered record only in case when record undo_no is less than undo_no
133 in the view. If this is not true we build based on undo_rec previous
134 version of the record. This record is found because purge can't remove
135 records accessed by active transaction. Thus we see correct version. Q. E. D.
136 -------------------------------------------------------------------------------
137 FACT C: Purge does not remove any delete-marked row that is visible
138 -------
139 in any cursor read view.
140 
141 PROOF: We know that:
142  1: Currently active read views in trx_sys_t::view_list are ordered by
143     ReadView::low_limit_no in descending order, that is,
144     newest read view first.
145 
146  2: Purge clones the oldest read view and uses that to determine whether there
147     are any active transactions that can see the to be purged records.
148 
149 Therefore any joining or active transaction will not have a view older
150 than the purge view, according to 1.
151 
152 When purge needs to remove a delete-marked row from a secondary index,
153 it will first check that the DB_TRX_ID value of the corresponding
154 record in the clustered index is older than the purge view. It will
155 also check if there is a newer version of the row (clustered index
156 record) that is not delete-marked in the secondary index. If such a
157 row exists and is collation-equal to the delete-marked secondary index
158 record then purge will not remove the secondary index record.
159 
160 Delete-marked clustered index records will be removed by
161 row_purge_remove_clust_if_poss(), unless the clustered index record
162 (and its DB_ROLL_PTR) has been updated. Every new version of the
163 clustered index record will update DB_ROLL_PTR, pointing to a new UNDO
164 log entry that allows the old version to be reconstructed. The
165 DB_ROLL_PTR in the oldest remaining version in the old-version chain
166 may be pointing to garbage (an undo log record discarded by purge),
167 but it will never be dereferenced, because the purge view is older
168 than any active transaction.
169 
170 For details see: row_vers_old_has_index_entry() and row_purge_poss_sec()
171 
172 Some additional issues:
173 
174 What if trx_sys->view_list == NULL and some transaction T1 and Purge both
175 try to open read_view at same time. Only one can acquire trx_sys->mutex.
176 In which order will the views be opened? Should it matter? If no, why?
177 
178 The order does not matter. No new transactions can be created and no running
179 RW transaction can commit or rollback (or free views). AC-NL-RO transactions
180 will mark their views as closed but not actually free their views.
181 */
182 
183 /** Minimum number of elements to reserve in ReadView::ids_t */
184 static const ulint MIN_TRX_IDS = 32;
185 
186 #ifdef UNIV_DEBUG
187 /** Functor to validate the view list. */
188 struct	ViewCheck {
189 
ViewCheckViewCheck190 	ViewCheck() : m_prev_view() { }
191 
operator ()ViewCheck192 	void	operator()(const ReadView* view)
193 	{
194 		ut_a(m_prev_view == NULL
195 		     || view->is_closed()
196 		     || view->le(m_prev_view));
197 
198 		m_prev_view = view;
199 	}
200 
201 	const ReadView*	m_prev_view;
202 };
203 
204 /**
205 Validates a read view list. */
206 
207 bool
validate() const208 MVCC::validate() const
209 {
210 	ViewCheck	check;
211 
212 	ut_ad(mutex_own(&trx_sys->mutex));
213 
214 	ut_list_map(m_views, check);
215 
216 	return(true);
217 }
218 #endif /* UNIV_DEBUG */
219 
220 /**
221 Try and increase the size of the array. Old elements are
222 copied across.
223 @param n 		Make space for n elements */
224 
225 void
reserve(ulint n)226 ReadView::ids_t::reserve(ulint n)
227 {
228 	if (n <= capacity()) {
229 		return;
230 	}
231 
232 	/** Keep a minimum threshold */
233 	if (n < MIN_TRX_IDS) {
234 		n = MIN_TRX_IDS;
235 	}
236 
237 	value_type*	p = m_ptr;
238 
239 	m_ptr = UT_NEW_ARRAY_NOKEY(value_type, n);
240 
241 	m_reserved = n;
242 
243 	ut_ad(size() < capacity());
244 
245 	if (p != NULL) {
246 
247 		::memmove(m_ptr, p, size() * sizeof(value_type));
248 
249 		UT_DELETE_ARRAY(p);
250 	}
251 }
252 
253 /**
254 Copy and overwrite this array contents
255 @param start		Source array
256 @param end		Pointer to end of array */
257 
258 void
assign(const value_type * start,const value_type * end)259 ReadView::ids_t::assign(const value_type* start, const value_type* end)
260 {
261 	ut_ad(end >= start);
262 
263 	ulint	n = end - start;
264 
265 	/* No need to copy the old contents across during reserve(). */
266 	clear();
267 
268 	/* Create extra space if required. */
269 	reserve(n);
270 
271 	resize(n);
272 
273 	ut_ad(size() == n);
274 
275 	::memmove(m_ptr, start, size() * sizeof(value_type));
276 }
277 
278 /**
279 Append a value to the array.
280 @param value		the value to append */
281 
282 void
push_back(value_type value)283 ReadView::ids_t::push_back(value_type value)
284 {
285 	if (capacity() <= size()) {
286 		reserve(size() * 2);
287 	}
288 
289 	m_ptr[m_size++] = value;
290 	ut_ad(size() <= capacity());
291 }
292 
293 /**
294 Insert the value in the correct slot, preserving the order. Doesn't
295 check for duplicates. */
296 
297 void
insert(value_type value)298 ReadView::ids_t::insert(value_type value)
299 {
300 	ut_ad(value > 0);
301 
302 	reserve(size() + 1);
303 
304 	if (empty() || back() < value) {
305 		push_back(value);
306 		return;
307 	}
308 
309 	value_type*	end = data() + size();
310 	value_type*	ub = std::upper_bound(data(), end, value);
311 
312 	if (ub == end) {
313 		push_back(value);
314 	} else {
315 		ut_ad(ub < end);
316 
317 		ulint	n_elems = std::distance(ub, end);
318 		ulint	n = n_elems * sizeof(value_type);
319 
320 		/* Note: Copying overlapped memory locations. */
321 		::memmove(ub + 1, ub, n);
322 
323 		*ub = value;
324 
325 		resize(size() + 1);
326 	}
327 }
328 
329 /**
330 ReadView constructor */
ReadView()331 ReadView::ReadView()
332 	:
333 	m_low_limit_id(),
334 	m_up_limit_id(),
335 	m_creator_trx_id(),
336 	m_ids(),
337 	m_low_limit_no(),
338 	m_cloned(false)
339 {
340 	ut_d(::memset(&m_view_list, 0x0, sizeof(m_view_list)));
341 }
342 
343 /**
344 ReadView destructor */
~ReadView()345 ReadView::~ReadView()
346 {
347 	// Do nothing
348 }
349 
350 /** Constructor
351 @param size		Number of views to pre-allocate */
MVCC(ulint size)352 MVCC::MVCC(ulint size)
353 {
354 	UT_LIST_INIT(m_free, &ReadView::m_view_list);
355 	UT_LIST_INIT(m_views, &ReadView::m_view_list);
356 
357 	for (ulint i = 0; i < size; ++i) {
358 		ReadView*	view = UT_NEW_NOKEY(ReadView());
359 
360 		UT_LIST_ADD_FIRST(m_free, view);
361 	}
362 }
363 
~MVCC()364 MVCC::~MVCC()
365 {
366 	for (ReadView* view = UT_LIST_GET_FIRST(m_free);
367 	     view != NULL;
368 	     view = UT_LIST_GET_FIRST(m_free)) {
369 
370 		UT_LIST_REMOVE(m_free, view);
371 
372 		UT_DELETE(view);
373 	}
374 
375 	ut_a(UT_LIST_GET_LEN(m_views) == 0);
376 }
377 
378 /** Insert the view in the proper order into the view list.
379 @param	view	view to add */
380 void
view_add(const ReadView * view)381 MVCC::view_add(const ReadView* view)
382 {
383 	ut_ad(trx_sys_mutex_own());
384 
385 	UT_LIST_ADD_FIRST(m_views, const_cast<ReadView *>(view));
386 
387 	ut_ad(!view->is_closed());
388 
389 	ut_ad(validate());
390 }
391 
392 /**
393 Copy the transaction ids from the source vector */
394 
395 void
copy_trx_ids(const trx_ids_t & trx_ids)396 ReadView::copy_trx_ids(const trx_ids_t& trx_ids)
397 {
398 	ut_ad(!m_cloned);
399 	ulint	size = trx_ids.size();
400 
401 	if (m_creator_trx_id > 0) {
402 		ut_ad(size > 0);
403 		--size;
404 	}
405 
406 	if (size == 0) {
407 		m_ids.clear();
408 		return;
409 	}
410 
411 	m_ids.reserve(size);
412 	m_ids.resize(size);
413 
414 	ids_t::value_type*	p = m_ids.data();
415 
416 	/* Copy all the trx_ids except the creator trx id */
417 
418 	if (m_creator_trx_id > 0) {
419 
420 		/* Note: We go through all this trouble because it is
421 		unclear whether std::vector::resize() will cause an
422 		overhead or not. We should test this extensively and
423 		if the vector to vector copy is fast enough then get
424 		rid of this code and replace it with more readable
425 		and obvious code. The code below does exactly one copy,
426 		and filters out the creator's trx id. */
427 
428 		trx_ids_t::const_iterator	it = std::lower_bound(
429 			trx_ids.begin(), trx_ids.end(), m_creator_trx_id);
430 
431 		ut_ad(it != trx_ids.end() && *it == m_creator_trx_id);
432 
433 		ulint	i = std::distance(trx_ids.begin(), it);
434 		ulint	n = i * sizeof(trx_ids_t::value_type);
435 
436 		::memmove(p, &trx_ids[0], n);
437 
438 		n = (trx_ids.size() - i - 1) * sizeof(trx_ids_t::value_type);
439 
440 		ut_ad(i + (n / sizeof(trx_ids_t::value_type)) == m_ids.size());
441 
442 		if (n > 0) {
443 			::memmove(p + i, &trx_ids[i + 1], n);
444 		}
445 	} else {
446 		ulint	n = size * sizeof(trx_ids_t::value_type);
447 
448 		::memmove(p, &trx_ids[0], n);
449 	}
450 
451 #ifdef UNIV_DEBUG
452 	/* Assert that all transaction ids in list are active. */
453 	for (trx_ids_t::const_iterator it = trx_ids.begin();
454 	     it != trx_ids.end(); ++it) {
455 
456 		trx_t*	trx = trx_get_rw_trx_by_id(*it);
457 		ut_ad(trx != NULL);
458 		ut_ad(trx->state == TRX_STATE_ACTIVE
459 		      || trx->state == TRX_STATE_PREPARED);
460 	}
461 #endif /* UNIV_DEBUG */
462 }
463 
464 /**
465 Opens a read view where exactly the transactions serialized before this
466 point in time are seen in the view.
467 @param id		Creator transaction id */
468 
469 void
prepare(trx_id_t id)470 ReadView::prepare(trx_id_t id)
471 {
472 	ut_ad(!m_cloned);
473 	ut_ad(mutex_own(&trx_sys->mutex));
474 
475 	m_creator_trx_id = id;
476 
477 	m_low_limit_no = m_low_limit_id = trx_sys->max_trx_id;
478 
479 	if (!trx_sys->rw_trx_ids.empty()) {
480 		copy_trx_ids(trx_sys->rw_trx_ids);
481 	} else {
482 		m_ids.clear();
483 	}
484 
485 	if (UT_LIST_GET_LEN(trx_sys->serialisation_list) > 0) {
486 		const trx_t*	trx;
487 
488 		trx = UT_LIST_GET_FIRST(trx_sys->serialisation_list);
489 
490 		if (trx->no < m_low_limit_no) {
491 			m_low_limit_no = trx->no;
492 		}
493 	}
494 }
495 
496 /**
497 Complete the read view creation */
498 
499 void
complete()500 ReadView::complete()
501 {
502 	ut_ad(!m_cloned);
503 	/* The first active transaction has the smallest id. */
504 	m_up_limit_id = !m_ids.empty() ? m_ids.front() : m_low_limit_id;
505 
506 	ut_ad(m_up_limit_id <= m_low_limit_id);
507 
508 	m_closed = false;
509 }
510 
511 /**
512 Find a free view from the active list, if none found then allocate
513 a new view.
514 @return a view to use */
515 
516 ReadView*
get_view()517 MVCC::get_view()
518 {
519 	ut_ad(mutex_own(&trx_sys->mutex));
520 
521 	ReadView*	view;
522 
523 	if (UT_LIST_GET_LEN(m_free) > 0) {
524 		view = UT_LIST_GET_FIRST(m_free);
525 		UT_LIST_REMOVE(m_free, view);
526 	} else {
527 		view = UT_NEW_NOKEY(ReadView());
528 
529 		if (view == NULL) {
530 			ib::error() << "Failed to allocate MVCC view";
531 		}
532 	}
533 
534 	return(view);
535 }
536 
537 /**
538 Release a view that is inactive but not closed. Caller must own
539 the trx_sys_t::mutex.
540 @param view		View to release */
541 void
view_release(ReadView * & view)542 MVCC::view_release(ReadView*& view)
543 {
544 	ut_ad(!srv_read_only_mode);
545 	ut_ad(trx_sys_mutex_own());
546 
547 	uintptr_t	p = reinterpret_cast<uintptr_t>(view);
548 
549 	ut_a(p & 0x1);
550 
551 	view = reinterpret_cast<ReadView*>(p & ~1);
552 
553 	ut_ad(view->m_closed);
554 	ut_ad(!view->m_cloned);
555 
556 	/** RW transactions should not free their views here. Their views
557 	should freed using view_close_view() */
558 
559 	ut_ad(view->m_creator_trx_id == 0);
560 
561 	UT_LIST_REMOVE(m_views, view);
562 
563 	UT_LIST_ADD_LAST(m_free, view);
564 
565 	view = NULL;
566 }
567 
568 /**
569 Allocate and create a view.
570 @param view		view owned by this class created for the
571 			caller. Must be freed by calling view_close()
572 @param trx		transaction instance of caller */
573 void
view_open(ReadView * & view,trx_t * trx)574 MVCC::view_open(ReadView*& view, trx_t* trx)
575 {
576 	ut_ad(!srv_read_only_mode);
577 
578 	/** If no new RW transaction has been started since the last view
579 	was created then reuse the the existing view. */
580 	if (view != NULL) {
581 
582 		uintptr_t	p = reinterpret_cast<uintptr_t>(view);
583 
584 		view = reinterpret_cast<ReadView*>(p & ~1);
585 
586 		ut_ad(view->m_closed);
587 
588 		/* NOTE: This can be optimised further, for now we only
589 		resuse the view iff there are no active RW transactions.
590 
591 		There is an inherent race here between purge and this
592 		thread. Purge will skip views that are marked as closed.
593 		Therefore we must set the low limit id after we reset the
594 		closed status after the check. */
595 
596 		if (trx_is_autocommit_non_locking(trx) && view->empty()) {
597 
598 			view->m_closed = false;
599 
600 			if (view->m_low_limit_id == trx_sys_get_max_trx_id()) {
601 				return;
602 			} else {
603 				view->m_closed = true;
604 			}
605 		}
606 
607 		mutex_enter(&trx_sys->mutex);
608 
609 		UT_LIST_REMOVE(m_views, view);
610 
611 	} else {
612 		mutex_enter(&trx_sys->mutex);
613 
614 		view = get_view();
615 	}
616 
617 	if (view != NULL) {
618 
619 		view->prepare(trx->id);
620 
621 		view->complete();
622 
623 		view_add(view);
624 
625 	}
626 
627 	trx_sys_mutex_exit();
628 }
629 
630 /**
631 Get the oldest (active) view in the system.
632 @return oldest view if found or NULL */
633 
634 ReadView*
get_oldest_view() const635 MVCC::get_oldest_view() const
636 {
637 	ReadView*	view;
638 
639 	ut_ad(mutex_own(&trx_sys->mutex));
640 
641 	for (view = UT_LIST_GET_LAST(m_views);
642 	     view != NULL;
643 	     view = UT_LIST_GET_PREV(m_view_list, view)) {
644 
645 		if (!view->is_closed()) {
646 			break;
647 		}
648 	}
649 
650 	return(view);
651 }
652 
653 /**
654 Copy state from another view. Must call copy_complete() to finish.
655 @param other		view to copy from */
656 
657 void
copy_prepare(const ReadView & other)658 ReadView::copy_prepare(const ReadView& other)
659 {
660 	ut_ad(&other != this);
661 
662 	if (!other.m_ids.empty()) {
663 		const ids_t::value_type* 	p = other.m_ids.data();
664 
665 		m_ids.assign(p, p + other.m_ids.size());
666 	} else {
667 		m_ids.clear();
668 	}
669 
670 	m_up_limit_id = other.m_up_limit_id;
671 
672 	m_low_limit_no = other.m_low_limit_no;
673 
674 	m_low_limit_id = other.m_low_limit_id;
675 
676 	m_creator_trx_id = other.m_creator_trx_id;
677 }
678 
679 /**
680 Complete the copy, insert the creator transaction id into the
681 m_ids too and adjust the m_up_limit_id, if required */
682 
683 void
copy_complete()684 ReadView::copy_complete()
685 {
686 	ut_ad(!trx_sys_mutex_own());
687 
688 	if (m_creator_trx_id > 0) {
689 		m_ids.insert(m_creator_trx_id);
690 	}
691 
692 	if (!m_ids.empty()) {
693 		/* The last active transaction has the smallest id. */
694 		m_up_limit_id = std::min(m_ids.front(), m_up_limit_id);
695 	}
696 
697 	ut_ad(m_up_limit_id <= m_low_limit_id);
698 
699 	/* We added the creator transaction ID to the m_ids. */
700 	m_creator_trx_id = 0;
701 }
702 
703 /**
704 Clones a read view object. The resulting read view has identical change
705 visibility as the donor read view
706 @param	result	pointer to resulting read view. If NULL, a view will be
707         allocated. If non-NULL, a view will overwrite a previously-existing
708         in-use or released view.
709 @param	from_trx	transation owning the donor read view. */
710 
711 void
clone(ReadView * & result,trx_t * from_trx) const712 ReadView::clone(ReadView*& result, trx_t* from_trx) const
713 {
714 	ut_ad(from_trx->read_view == this);
715 	ut_ad(trx_sys_mutex_own());
716 
717 	if (!result)
718 		result = trx_sys->mvcc->get_view();
719 	else {
720 		result = reinterpret_cast<ReadView *>
721 			(reinterpret_cast<uintptr_t>(result) & ~1);
722 	}
723 
724 	// Set the creating trx id of the clone to that of donor.
725 	trx_id_t from_trx_id;
726 	if (from_trx->read_view->m_creator_trx_id != 0) {
727 		// The donor transaction is RO, and a clone itself
728 		from_trx_id = from_trx->read_view->m_creator_trx_id;
729 	} else if (from_trx->id == 0) {
730 		// The donor transaction is RO, thus does not have a trx ID
731 		// yet which the cloned view must see, if it assigned later
732 		if (!from_trx->preallocated_id) {
733 			// Preallocate a transaction id for the donor
734 			from_trx_id = from_trx->preallocated_id
735 				= trx_sys_get_new_trx_id();
736 		} else {
737 			// This transaction has already been cloned
738 			from_trx_id = from_trx->preallocated_id;
739 		}
740 	} else {
741 		// The donor transaction is RW
742 		from_trx_id = from_trx->id;
743 	}
744 
745 	result->copy_prepare(*this);
746 	// Calling copy_complete would be redundant for us and would force
747 	// a too early trx sys mutex release.
748 	result->m_creator_trx_id = from_trx_id;
749 	// If the clone transaction is RO and is later promoted to RW, make
750 	// sure not to add its own id to its view
751 	result->m_cloned = true;
752 	result->m_closed = false;
753 }
754 
755 /** Clones the oldest view and stores it in view. No need to
756 call view_close(). The caller owns the view that is passed in.
757 This function is called by Purge to determine whether it should
758 purge the delete marked record or not.
759 @param view		Preallocated view, owned by the caller */
760 
761 void
clone_oldest_view(ReadView * view)762 MVCC::clone_oldest_view(ReadView* view)
763 {
764 	mutex_enter(&trx_sys->mutex);
765 
766 	ReadView*	oldest_view = get_oldest_view();
767 
768 	if (oldest_view == NULL) {
769 
770 		view->prepare(0);
771 
772 		trx_sys_mutex_exit();
773 
774 		view->complete();
775 
776 	} else {
777 		view->copy_prepare(*oldest_view);
778 
779 		trx_sys_mutex_exit();
780 
781 		view->copy_complete();
782 	}
783 }
784 
785 /**
786 @return the number of active views */
787 
788 ulint
size() const789 MVCC::size() const
790 {
791 	trx_sys_mutex_enter();
792 
793 	ulint	size = 0;
794 
795 	for (const ReadView* view = UT_LIST_GET_FIRST(m_views);
796 	     view != NULL;
797 	     view = UT_LIST_GET_NEXT(m_view_list, view)) {
798 
799 		if (!view->is_closed()) {
800 			++size;
801 		}
802 	}
803 
804 	trx_sys_mutex_exit();
805 
806 	return(size);
807 }
808 
809 /**
810 Close a view created by the above function.
811 @para view		view allocated by trx_open.
812 @param own_mutex	true if caller owns trx_sys_t::mutex */
813 
814 void
view_close(ReadView * & view,bool own_mutex)815 MVCC::view_close(ReadView*& view, bool own_mutex)
816 {
817 	uintptr_t	p = reinterpret_cast<uintptr_t>(view);
818 
819 	/* Note: The assumption here is that AC-NL-RO transactions will
820 	call this function with own_mutex == false. */
821 	if (!own_mutex) {
822 		/* Sanitise the pointer first. */
823 		ReadView*	ptr = reinterpret_cast<ReadView*>(p & ~1);
824 
825 		/* Note this can be called for a read view that
826 		was already closed. */
827 		ptr->m_closed = true;
828 		ptr->m_cloned = false;
829 
830 		/* Set the view as closed. */
831 		view = reinterpret_cast<ReadView*>(p | 0x1);
832 	} else {
833 		view = reinterpret_cast<ReadView*>(p & ~1);
834 
835 		view->close();
836 
837 		UT_LIST_REMOVE(m_views, view);
838 		UT_LIST_ADD_LAST(m_free, view);
839 
840 		ut_ad(validate());
841 
842 		view = NULL;
843 	}
844 }
845 
846 /**
847 Set the view creator transaction id. Note: This shouldbe set only
848 for views created by RW transactions.
849 @param view		Set the creator trx id for this view
850 @param id		Transaction id to set */
851 
852 void
set_view_creator_trx_id(ReadView * view,trx_id_t id)853 MVCC::set_view_creator_trx_id(ReadView* view, trx_id_t id)
854 {
855 	ut_ad(!view->is_cloned());
856 	ut_ad(id > 0);
857 	ut_ad(mutex_own(&trx_sys->mutex));
858 
859 	view->creator_trx_id(id);
860 }
861 
862 i_s_xtradb_read_view_t*
read_fill_i_s_xtradb_read_view(i_s_xtradb_read_view_t * rv)863 read_fill_i_s_xtradb_read_view(i_s_xtradb_read_view_t* rv)
864 {
865 	ReadView*    view;
866 
867 	mutex_enter(&trx_sys->mutex);
868 
869 	view = trx_sys->mvcc->get_oldest_view();
870 	if (!view) {
871 		mutex_exit(&trx_sys->mutex);
872 		return NULL;
873 	}
874 
875 	rv->low_limit_no = view->low_limit_no();
876 	rv->up_limit_id = view->up_limit_id();
877 	rv->low_limit_id = view->low_limit_id();
878 
879 	mutex_exit(&trx_sys->mutex);
880 
881 	return rv;
882 }
883