641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
1 |
/*****************************************************************************
|
2 |
||
3 |
Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
|
|
4 |
||
5 |
This program is free software; you can redistribute it and/or modify it under
|
|
6 |
the terms of the GNU General Public License as published by the Free Software
|
|
7 |
Foundation; version 2 of the License.
|
|
8 |
||
9 |
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
10 |
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11 |
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
12 |
||
13 |
You should have received a copy of the GNU General Public License along with
|
|
1802.10.2
by Monty Taylor
Update all of the copyright headers to include the correct address. |
14 |
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
|
15 |
St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
16 |
|
17 |
*****************************************************************************/
|
|
18 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
19 |
/**************************************************//**
|
20 |
@file row/row0vers.c
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
21 |
Row versions
|
22 |
||
23 |
Created 2/6/1997 Heikki Tuuri
|
|
24 |
*******************************************************/
|
|
25 |
||
26 |
#include "row0vers.h" |
|
27 |
||
28 |
#ifdef UNIV_NONINL
|
|
29 |
#include "row0vers.ic" |
|
30 |
#endif
|
|
31 |
||
32 |
#include "dict0dict.h" |
|
33 |
#include "dict0boot.h" |
|
34 |
#include "btr0btr.h" |
|
35 |
#include "mach0data.h" |
|
36 |
#include "trx0rseg.h" |
|
37 |
#include "trx0trx.h" |
|
38 |
#include "trx0roll.h" |
|
39 |
#include "trx0undo.h" |
|
40 |
#include "trx0purge.h" |
|
41 |
#include "trx0rec.h" |
|
42 |
#include "que0que.h" |
|
43 |
#include "row0row.h" |
|
44 |
#include "row0upd.h" |
|
45 |
#include "rem0cmp.h" |
|
46 |
#include "read0read.h" |
|
47 |
#include "lock0lock.h" |
|
48 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
49 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
50 |
Finds out if an active transaction has inserted or modified a secondary
|
51 |
index record. NOTE: the kernel mutex is temporarily released in this
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
52 |
function!
|
53 |
@return NULL if committed, else the active transaction */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
54 |
UNIV_INTERN
|
55 |
trx_t* |
|
56 |
row_vers_impl_x_locked_off_kernel( |
|
57 |
/*==============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
58 |
const rec_t* rec, /*!< in: record in a secondary index */ |
59 |
dict_index_t* index, /*!< in: the secondary index */ |
|
60 |
const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
61 |
{
|
62 |
dict_index_t* clust_index; |
|
63 |
rec_t* clust_rec; |
|
64 |
ulint* clust_offsets; |
|
65 |
rec_t* version; |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
66 |
trx_id_t trx_id; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
67 |
mem_heap_t* heap; |
68 |
mem_heap_t* heap2; |
|
69 |
dtuple_t* row; |
|
70 |
dtuple_t* entry = NULL; /* assignment to eliminate compiler |
|
71 |
warning */
|
|
72 |
trx_t* trx; |
|
73 |
ulint rec_del; |
|
74 |
ulint err; |
|
75 |
mtr_t mtr; |
|
76 |
ulint comp; |
|
77 |
||
78 |
ut_ad(mutex_own(&kernel_mutex)); |
|
79 |
#ifdef UNIV_SYNC_DEBUG
|
|
80 |
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); |
|
81 |
#endif /* UNIV_SYNC_DEBUG */ |
|
82 |
||
83 |
mutex_exit(&kernel_mutex); |
|
84 |
||
85 |
mtr_start(&mtr); |
|
86 |
||
87 |
/* Search for the clustered index record: this is a time-consuming
|
|
88 |
operation: therefore we release the kernel mutex; also, the release
|
|
89 |
is required by the latching order convention. The latch on the
|
|
90 |
clustered index locks the top of the stack of versions. We also
|
|
91 |
reserve purge_latch to lock the bottom of the version stack. */
|
|
92 |
||
93 |
clust_rec = row_get_clust_rec(BTR_SEARCH_LEAF, rec, index, |
|
94 |
&clust_index, &mtr); |
|
95 |
if (!clust_rec) { |
|
96 |
/* In a rare case it is possible that no clust rec is found
|
|
97 |
for a secondary index record: if in row0umod.c
|
|
98 |
row_undo_mod_remove_clust_low() we have already removed the
|
|
99 |
clust rec, while purge is still cleaning and removing
|
|
100 |
secondary index records associated with earlier versions of
|
|
101 |
the clustered index record. In that case there cannot be
|
|
102 |
any implicit lock on the secondary index record, because
|
|
103 |
an active transaction which has modified the secondary index
|
|
104 |
record has also modified the clustered index record. And in
|
|
105 |
a rollback we always undo the modifications to secondary index
|
|
106 |
records before the clustered index record. */
|
|
107 |
||
108 |
mutex_enter(&kernel_mutex); |
|
109 |
mtr_commit(&mtr); |
|
110 |
||
111 |
return(NULL); |
|
112 |
}
|
|
113 |
||
114 |
heap = mem_heap_create(1024); |
|
115 |
clust_offsets = rec_get_offsets(clust_rec, clust_index, NULL, |
|
116 |
ULINT_UNDEFINED, &heap); |
|
117 |
trx_id = row_get_rec_trx_id(clust_rec, clust_index, clust_offsets); |
|
118 |
||
119 |
mtr_s_lock(&(purge_sys->latch), &mtr); |
|
120 |
||
121 |
mutex_enter(&kernel_mutex); |
|
122 |
||
123 |
trx = NULL; |
|
124 |
if (!trx_is_active(trx_id)) { |
|
125 |
/* The transaction that modified or inserted clust_rec is no
|
|
126 |
longer active: no implicit lock on rec */
|
|
127 |
goto exit_func; |
|
128 |
}
|
|
129 |
||
130 |
if (!lock_check_trx_id_sanity(trx_id, clust_rec, clust_index, |
|
131 |
clust_offsets, TRUE)) { |
|
132 |
/* Corruption noticed: try to avoid a crash by returning */
|
|
133 |
goto exit_func; |
|
134 |
}
|
|
135 |
||
136 |
comp = page_rec_is_comp(rec); |
|
137 |
ut_ad(index->table == clust_index->table); |
|
138 |
ut_ad(!!comp == dict_table_is_comp(index->table)); |
|
139 |
ut_ad(!comp == !page_rec_is_comp(clust_rec)); |
|
140 |
||
141 |
/* We look up if some earlier version, which was modified by the trx_id
|
|
142 |
transaction, of the clustered index record would require rec to be in
|
|
143 |
a different state (delete marked or unmarked, or have different field
|
|
144 |
values, or not existing). If there is such a version, then rec was
|
|
145 |
modified by the trx_id transaction, and it has an implicit x-lock on
|
|
146 |
rec. Note that if clust_rec itself would require rec to be in a
|
|
147 |
different state, then the trx_id transaction has not yet had time to
|
|
148 |
modify rec, and does not necessarily have an implicit x-lock on rec. */
|
|
149 |
||
150 |
rec_del = rec_get_deleted_flag(rec, comp); |
|
151 |
trx = NULL; |
|
152 |
||
153 |
version = clust_rec; |
|
154 |
||
155 |
for (;;) { |
|
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
156 |
rec_t* prev_version; |
157 |
ulint vers_del; |
|
158 |
row_ext_t* ext; |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
159 |
trx_id_t prev_trx_id; |
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
160 |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
161 |
mutex_exit(&kernel_mutex); |
162 |
||
163 |
/* While we retrieve an earlier version of clust_rec, we
|
|
164 |
release the kernel mutex, because it may take time to access
|
|
165 |
the disk. After the release, we have to check if the trx_id
|
|
166 |
transaction is still active. We keep the semaphore in mtr on
|
|
167 |
the clust_rec page, so that no other transaction can update
|
|
168 |
it and get an implicit x-lock on rec. */
|
|
169 |
||
170 |
heap2 = heap; |
|
171 |
heap = mem_heap_create(1024); |
|
172 |
err = trx_undo_prev_version_build(clust_rec, &mtr, version, |
|
173 |
clust_index, clust_offsets, |
|
174 |
heap, &prev_version); |
|
175 |
mem_heap_free(heap2); /* free version and clust_offsets */ |
|
176 |
||
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
177 |
if (prev_version == NULL) { |
178 |
mutex_enter(&kernel_mutex); |
|
179 |
||
180 |
if (!trx_is_active(trx_id)) { |
|
181 |
/* Transaction no longer active: no
|
|
182 |
implicit x-lock */
|
|
183 |
||
641.2.1
by Monty Taylor
InnoDB Plugin 1.0.2 |
184 |
break; |
185 |
}
|
|
186 |
||
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
187 |
/* If the transaction is still active,
|
188 |
clust_rec must be a fresh insert, because no
|
|
189 |
previous version was found. */
|
|
190 |
ut_ad(err == DB_SUCCESS); |
|
191 |
||
192 |
/* It was a freshly inserted version: there is an
|
|
193 |
implicit x-lock on rec */
|
|
194 |
||
195 |
trx = trx_get_on_id(trx_id); |
|
196 |
||
197 |
break; |
|
198 |
}
|
|
199 |
||
200 |
clust_offsets = rec_get_offsets(prev_version, clust_index, |
|
201 |
NULL, ULINT_UNDEFINED, &heap); |
|
202 |
||
203 |
vers_del = rec_get_deleted_flag(prev_version, comp); |
|
204 |
prev_trx_id = row_get_rec_trx_id(prev_version, clust_index, |
|
205 |
clust_offsets); |
|
206 |
||
207 |
/* If the trx_id and prev_trx_id are different and if
|
|
208 |
the prev_version is marked deleted then the
|
|
209 |
prev_trx_id must have already committed for the trx_id
|
|
210 |
to be able to modify the row. Therefore, prev_trx_id
|
|
211 |
cannot hold any implicit lock. */
|
|
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
212 |
if (vers_del && trx_id != prev_trx_id) { |
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
213 |
|
214 |
mutex_enter(&kernel_mutex); |
|
215 |
break; |
|
216 |
}
|
|
217 |
||
218 |
/* The stack of versions is locked by mtr. Thus, it
|
|
219 |
is safe to fetch the prefixes for externally stored
|
|
220 |
columns. */
|
|
221 |
row = row_build(ROW_COPY_POINTERS, clust_index, prev_version, |
|
222 |
clust_offsets, NULL, &ext, heap); |
|
223 |
entry = row_build_index_entry(row, ext, index, heap); |
|
224 |
/* entry may be NULL if a record was inserted in place
|
|
225 |
of a deleted record, and the BLOB pointers of the new
|
|
226 |
record were not initialized yet. But in that case,
|
|
227 |
prev_version should be NULL. */
|
|
228 |
ut_a(entry); |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
229 |
|
230 |
mutex_enter(&kernel_mutex); |
|
231 |
||
232 |
if (!trx_is_active(trx_id)) { |
|
233 |
/* Transaction no longer active: no implicit x-lock */
|
|
234 |
||
235 |
break; |
|
236 |
}
|
|
237 |
||
238 |
/* If we get here, we know that the trx_id transaction is
|
|
239 |
still active and it has modified prev_version. Let us check
|
|
240 |
if prev_version would require rec to be in a different
|
|
241 |
state. */
|
|
242 |
||
641.2.2
by Monty Taylor
InnoDB Plugin 1.0.3 |
243 |
/* The previous version of clust_rec must be
|
244 |
accessible, because the transaction is still active
|
|
245 |
and clust_rec was not a fresh insert. */
|
|
246 |
ut_ad(err == DB_SUCCESS); |
|
247 |
||
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
248 |
/* We check if entry and rec are identified in the alphabetical
|
249 |
ordering */
|
|
250 |
if (0 == cmp_dtuple_rec(entry, rec, offsets)) { |
|
251 |
/* The delete marks of rec and prev_version should be
|
|
252 |
equal for rec to be in the state required by
|
|
253 |
prev_version */
|
|
254 |
||
255 |
if (rec_del != vers_del) { |
|
256 |
trx = trx_get_on_id(trx_id); |
|
257 |
||
258 |
break; |
|
259 |
}
|
|
260 |
||
261 |
/* It is possible that the row was updated so that the
|
|
262 |
secondary index record remained the same in
|
|
263 |
alphabetical ordering, but the field values changed
|
|
264 |
still. For example, 'abc' -> 'ABC'. Check also that. */
|
|
265 |
||
266 |
dtuple_set_types_binary(entry, |
|
267 |
dtuple_get_n_fields(entry)); |
|
268 |
if (0 != cmp_dtuple_rec(entry, rec, offsets)) { |
|
269 |
||
270 |
trx = trx_get_on_id(trx_id); |
|
271 |
||
272 |
break; |
|
273 |
}
|
|
274 |
} else if (!rec_del) { |
|
275 |
/* The delete mark should be set in rec for it to be
|
|
276 |
in the state required by prev_version */
|
|
277 |
||
278 |
trx = trx_get_on_id(trx_id); |
|
279 |
||
280 |
break; |
|
281 |
}
|
|
282 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
283 |
if (trx_id != prev_trx_id) { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
284 |
/* The versions modified by the trx_id transaction end
|
285 |
to prev_version: no implicit x-lock */
|
|
286 |
||
287 |
break; |
|
288 |
}
|
|
289 |
||
290 |
version = prev_version; |
|
291 |
}/* for (;;) */ |
|
292 |
||
293 |
exit_func: |
|
294 |
mtr_commit(&mtr); |
|
295 |
mem_heap_free(heap); |
|
296 |
||
297 |
return(trx); |
|
298 |
}
|
|
299 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
300 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
301 |
Finds out if we must preserve a delete marked earlier version of a clustered
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
302 |
index record, because it is >= the purge view.
|
303 |
@return TRUE if earlier version should be preserved */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
304 |
UNIV_INTERN
|
305 |
ibool
|
|
306 |
row_vers_must_preserve_del_marked( |
|
307 |
/*==============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
308 |
trx_id_t trx_id, /*!< in: transaction id in the version */ |
309 |
mtr_t* mtr) /*!< in: mtr holding the latch on the |
|
310 |
clustered index record; it will also
|
|
311 |
hold the latch on purge_view */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
312 |
{
|
313 |
#ifdef UNIV_SYNC_DEBUG
|
|
314 |
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); |
|
315 |
#endif /* UNIV_SYNC_DEBUG */ |
|
316 |
||
317 |
mtr_s_lock(&(purge_sys->latch), mtr); |
|
318 |
||
319 |
if (trx_purge_update_undo_must_exist(trx_id)) { |
|
320 |
||
321 |
/* A purge operation is not yet allowed to remove this
|
|
322 |
delete marked record */
|
|
323 |
||
324 |
return(TRUE); |
|
325 |
}
|
|
326 |
||
327 |
return(FALSE); |
|
328 |
}
|
|
329 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
330 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
331 |
Finds out if a version of the record, where the version >= the current
|
332 |
purge view, should have ientry as its secondary index entry. We check
|
|
333 |
if there is any not delete marked version of the record where the trx
|
|
334 |
id >= purge view, and the secondary index entry and ientry are identified in
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
335 |
the alphabetical ordering; exactly in this case we return TRUE.
|
336 |
@return TRUE if earlier version should have */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
337 |
UNIV_INTERN
|
338 |
ibool
|
|
339 |
row_vers_old_has_index_entry( |
|
340 |
/*=========================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
341 |
ibool also_curr,/*!< in: TRUE if also rec is included in the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
342 |
versions to search; otherwise only versions
|
343 |
prior to it are searched */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
344 |
const rec_t* rec, /*!< in: record in the clustered index; the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
345 |
caller must have a latch on the page */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
346 |
mtr_t* mtr, /*!< in: mtr holding the latch on rec; it will |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
347 |
also hold the latch on purge_view */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
348 |
dict_index_t* index, /*!< in: the secondary index */ |
349 |
const dtuple_t* ientry) /*!< in: the secondary index entry */ |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
350 |
{
|
351 |
const rec_t* version; |
|
352 |
rec_t* prev_version; |
|
353 |
dict_index_t* clust_index; |
|
354 |
ulint* clust_offsets; |
|
355 |
mem_heap_t* heap; |
|
356 |
mem_heap_t* heap2; |
|
357 |
const dtuple_t* row; |
|
358 |
const dtuple_t* entry; |
|
359 |
ulint err; |
|
360 |
ulint comp; |
|
361 |
||
362 |
ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) |
|
363 |
|| mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); |
|
364 |
#ifdef UNIV_SYNC_DEBUG
|
|
365 |
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); |
|
366 |
#endif /* UNIV_SYNC_DEBUG */ |
|
367 |
mtr_s_lock(&(purge_sys->latch), mtr); |
|
368 |
||
369 |
clust_index = dict_table_get_first_index(index->table); |
|
370 |
||
371 |
comp = page_rec_is_comp(rec); |
|
372 |
ut_ad(!dict_table_is_comp(index->table) == !comp); |
|
373 |
heap = mem_heap_create(1024); |
|
374 |
clust_offsets = rec_get_offsets(rec, clust_index, NULL, |
|
375 |
ULINT_UNDEFINED, &heap); |
|
376 |
||
377 |
if (also_curr && !rec_get_deleted_flag(rec, comp)) { |
|
378 |
row_ext_t* ext; |
|
379 |
||
380 |
/* The stack of versions is locked by mtr.
|
|
381 |
Thus, it is safe to fetch the prefixes for
|
|
382 |
externally stored columns. */
|
|
383 |
row = row_build(ROW_COPY_POINTERS, clust_index, |
|
384 |
rec, clust_offsets, NULL, &ext, heap); |
|
385 |
entry = row_build_index_entry(row, ext, index, heap); |
|
386 |
||
387 |
/* If entry == NULL, the record contains unset BLOB
|
|
388 |
pointers. This must be a freshly inserted record. If
|
|
389 |
this is called from
|
|
390 |
row_purge_remove_sec_if_poss_low(), the thread will
|
|
391 |
hold latches on the clustered index and the secondary
|
|
392 |
index. Because the insert works in three steps:
|
|
393 |
||
394 |
(1) insert the record to clustered index
|
|
395 |
(2) store the BLOBs and update BLOB pointers
|
|
396 |
(3) insert records to secondary indexes
|
|
397 |
||
398 |
the purge thread can safely ignore freshly inserted
|
|
399 |
records and delete the secondary index record. The
|
|
400 |
thread that inserted the new record will be inserting
|
|
401 |
the secondary index records. */
|
|
402 |
||
403 |
/* NOTE that we cannot do the comparison as binary
|
|
404 |
fields because the row is maybe being modified so that
|
|
405 |
the clustered index record has already been updated to
|
|
406 |
a different binary value in a char field, but the
|
|
407 |
collation identifies the old and new value anyway! */
|
|
408 |
if (entry && !dtuple_coll_cmp(ientry, entry)) { |
|
409 |
||
410 |
mem_heap_free(heap); |
|
411 |
||
412 |
return(TRUE); |
|
413 |
}
|
|
414 |
}
|
|
415 |
||
416 |
version = rec; |
|
417 |
||
418 |
for (;;) { |
|
419 |
heap2 = heap; |
|
420 |
heap = mem_heap_create(1024); |
|
421 |
err = trx_undo_prev_version_build(rec, mtr, version, |
|
422 |
clust_index, clust_offsets, |
|
423 |
heap, &prev_version); |
|
424 |
mem_heap_free(heap2); /* free version and clust_offsets */ |
|
425 |
||
426 |
if (err != DB_SUCCESS || !prev_version) { |
|
427 |
/* Versions end here */
|
|
428 |
||
429 |
mem_heap_free(heap); |
|
430 |
||
431 |
return(FALSE); |
|
432 |
}
|
|
433 |
||
434 |
clust_offsets = rec_get_offsets(prev_version, clust_index, |
|
435 |
NULL, ULINT_UNDEFINED, &heap); |
|
436 |
||
437 |
if (!rec_get_deleted_flag(prev_version, comp)) { |
|
438 |
row_ext_t* ext; |
|
439 |
||
440 |
/* The stack of versions is locked by mtr.
|
|
441 |
Thus, it is safe to fetch the prefixes for
|
|
442 |
externally stored columns. */
|
|
443 |
row = row_build(ROW_COPY_POINTERS, clust_index, |
|
444 |
prev_version, clust_offsets, |
|
445 |
NULL, &ext, heap); |
|
446 |
entry = row_build_index_entry(row, ext, index, heap); |
|
447 |
||
448 |
/* If entry == NULL, the record contains unset
|
|
449 |
BLOB pointers. This must be a freshly
|
|
450 |
inserted record that we can safely ignore.
|
|
451 |
For the justification, see the comments after
|
|
452 |
the previous row_build_index_entry() call. */
|
|
453 |
||
454 |
/* NOTE that we cannot do the comparison as binary
|
|
455 |
fields because maybe the secondary index record has
|
|
456 |
already been updated to a different binary value in
|
|
457 |
a char field, but the collation identifies the old
|
|
458 |
and new value anyway! */
|
|
459 |
||
460 |
if (entry && !dtuple_coll_cmp(ientry, entry)) { |
|
461 |
||
462 |
mem_heap_free(heap); |
|
463 |
||
464 |
return(TRUE); |
|
465 |
}
|
|
466 |
}
|
|
467 |
||
468 |
version = prev_version; |
|
469 |
}
|
|
470 |
}
|
|
471 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
472 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
473 |
Constructs the version of a clustered index record which a consistent
|
474 |
read should see. We assume that the trx id stored in rec is such that
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
475 |
the consistent read should not see rec in its present version.
|
476 |
@return DB_SUCCESS or DB_MISSING_HISTORY */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
477 |
UNIV_INTERN
|
478 |
ulint
|
|
479 |
row_vers_build_for_consistent_read( |
|
480 |
/*===============================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
481 |
const rec_t* rec, /*!< in: record in a clustered index; the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
482 |
caller must have a latch on the page; this
|
483 |
latch locks the top of the stack of versions
|
|
484 |
of this records */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
485 |
mtr_t* mtr, /*!< in: mtr holding the latch on rec */ |
486 |
dict_index_t* index, /*!< in: the clustered index */ |
|
487 |
ulint** offsets,/*!< in/out: offsets returned by |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
488 |
rec_get_offsets(rec, index) */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
489 |
read_view_t* view, /*!< in: the consistent read view */ |
490 |
mem_heap_t** offset_heap,/*!< in/out: memory heap from which |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
491 |
the offsets are allocated */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
492 |
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
493 |
*old_vers is allocated; memory for possible
|
494 |
intermediate versions is allocated and freed
|
|
495 |
locally within the function */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
496 |
rec_t** old_vers)/*!< out, own: old version, or NULL if the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
497 |
record does not exist in the view, that is,
|
498 |
it was freshly inserted afterwards */
|
|
499 |
{
|
|
500 |
const rec_t* version; |
|
501 |
rec_t* prev_version; |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
502 |
trx_id_t trx_id; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
503 |
mem_heap_t* heap = NULL; |
504 |
byte* buf; |
|
505 |
ulint err; |
|
506 |
||
507 |
ut_ad(dict_index_is_clust(index)); |
|
508 |
ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) |
|
509 |
|| mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); |
|
510 |
#ifdef UNIV_SYNC_DEBUG
|
|
511 |
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); |
|
512 |
#endif /* UNIV_SYNC_DEBUG */ |
|
513 |
||
514 |
ut_ad(rec_offs_validate(rec, index, *offsets)); |
|
515 |
||
516 |
trx_id = row_get_rec_trx_id(rec, index, *offsets); |
|
517 |
||
518 |
ut_ad(!read_view_sees_trx_id(view, trx_id)); |
|
519 |
||
520 |
rw_lock_s_lock(&(purge_sys->latch)); |
|
521 |
version = rec; |
|
522 |
||
523 |
for (;;) { |
|
524 |
mem_heap_t* heap2 = heap; |
|
525 |
trx_undo_rec_t* undo_rec; |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
526 |
roll_ptr_t roll_ptr; |
527 |
undo_no_t undo_no; |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
528 |
heap = mem_heap_create(1024); |
529 |
||
530 |
/* If we have high-granularity consistent read view and
|
|
531 |
creating transaction of the view is the same as trx_id in
|
|
532 |
the record we see this record only in the case when
|
|
533 |
undo_no of the record is < undo_no in the view. */
|
|
534 |
||
535 |
if (view->type == VIEW_HIGH_GRANULARITY |
|
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
536 |
&& view->creator_trx_id == trx_id) { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
537 |
|
538 |
roll_ptr = row_get_rec_roll_ptr(version, index, |
|
539 |
*offsets); |
|
540 |
undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap); |
|
541 |
undo_no = trx_undo_rec_get_undo_no(undo_rec); |
|
542 |
mem_heap_empty(heap); |
|
543 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
544 |
if (view->undo_no > undo_no) { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
545 |
/* The view already sees this version: we can
|
546 |
copy it to in_heap and return */
|
|
547 |
||
548 |
buf = mem_heap_alloc(in_heap, |
|
549 |
rec_offs_size(*offsets)); |
|
550 |
*old_vers = rec_copy(buf, version, *offsets); |
|
551 |
rec_offs_make_valid(*old_vers, index, |
|
552 |
*offsets); |
|
553 |
err = DB_SUCCESS; |
|
554 |
||
555 |
break; |
|
556 |
}
|
|
557 |
}
|
|
558 |
||
559 |
err = trx_undo_prev_version_build(rec, mtr, version, index, |
|
560 |
*offsets, heap, |
|
561 |
&prev_version); |
|
562 |
if (heap2) { |
|
563 |
mem_heap_free(heap2); /* free version */ |
|
564 |
}
|
|
565 |
||
566 |
if (err != DB_SUCCESS) { |
|
567 |
break; |
|
568 |
}
|
|
569 |
||
570 |
if (prev_version == NULL) { |
|
571 |
/* It was a freshly inserted version */
|
|
572 |
*old_vers = NULL; |
|
573 |
err = DB_SUCCESS; |
|
574 |
||
575 |
break; |
|
576 |
}
|
|
577 |
||
578 |
*offsets = rec_get_offsets(prev_version, index, *offsets, |
|
579 |
ULINT_UNDEFINED, offset_heap); |
|
580 |
||
581 |
trx_id = row_get_rec_trx_id(prev_version, index, *offsets); |
|
582 |
||
583 |
if (read_view_sees_trx_id(view, trx_id)) { |
|
584 |
||
585 |
/* The view already sees this version: we can copy
|
|
586 |
it to in_heap and return */
|
|
587 |
||
588 |
buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets)); |
|
589 |
*old_vers = rec_copy(buf, prev_version, *offsets); |
|
590 |
rec_offs_make_valid(*old_vers, index, *offsets); |
|
591 |
err = DB_SUCCESS; |
|
592 |
||
593 |
break; |
|
594 |
}
|
|
595 |
||
596 |
version = prev_version; |
|
597 |
}/* for (;;) */ |
|
598 |
||
599 |
mem_heap_free(heap); |
|
600 |
rw_lock_s_unlock(&(purge_sys->latch)); |
|
601 |
||
602 |
return(err); |
|
603 |
}
|
|
604 |
||
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
605 |
/*****************************************************************//**
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
606 |
Constructs the last committed version of a clustered index record,
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
607 |
which should be seen by a semi-consistent read.
|
608 |
@return DB_SUCCESS or DB_MISSING_HISTORY */
|
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
609 |
UNIV_INTERN
|
610 |
ulint
|
|
611 |
row_vers_build_for_semi_consistent_read( |
|
612 |
/*====================================*/
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
613 |
const rec_t* rec, /*!< in: record in a clustered index; the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
614 |
caller must have a latch on the page; this
|
615 |
latch locks the top of the stack of versions
|
|
616 |
of this records */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
617 |
mtr_t* mtr, /*!< in: mtr holding the latch on rec */ |
618 |
dict_index_t* index, /*!< in: the clustered index */ |
|
619 |
ulint** offsets,/*!< in/out: offsets returned by |
|
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
620 |
rec_get_offsets(rec, index) */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
621 |
mem_heap_t** offset_heap,/*!< in/out: memory heap from which |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
622 |
the offsets are allocated */
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
623 |
mem_heap_t* in_heap,/*!< in: memory heap from which the memory for |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
624 |
*old_vers is allocated; memory for possible
|
625 |
intermediate versions is allocated and freed
|
|
626 |
locally within the function */
|
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
627 |
const rec_t** old_vers)/*!< out: rec, old version, or NULL if the |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
628 |
record does not exist in the view, that is,
|
629 |
it was freshly inserted afterwards */
|
|
630 |
{
|
|
631 |
const rec_t* version; |
|
632 |
mem_heap_t* heap = NULL; |
|
633 |
byte* buf; |
|
634 |
ulint err; |
|
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
635 |
trx_id_t rec_trx_id = 0; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
636 |
|
637 |
ut_ad(dict_index_is_clust(index)); |
|
638 |
ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_X_FIX) |
|
639 |
|| mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX)); |
|
640 |
#ifdef UNIV_SYNC_DEBUG
|
|
641 |
ut_ad(!rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED)); |
|
642 |
#endif /* UNIV_SYNC_DEBUG */ |
|
643 |
||
644 |
ut_ad(rec_offs_validate(rec, index, *offsets)); |
|
645 |
||
646 |
rw_lock_s_lock(&(purge_sys->latch)); |
|
647 |
/* The S-latch on purge_sys prevents the purge view from
|
|
648 |
changing. Thus, if we have an uncommitted transaction at
|
|
649 |
this point, then purge cannot remove its undo log even if
|
|
650 |
the transaction could commit now. */
|
|
651 |
||
652 |
version = rec; |
|
653 |
||
654 |
for (;;) { |
|
655 |
trx_t* version_trx; |
|
656 |
mem_heap_t* heap2; |
|
657 |
rec_t* prev_version; |
|
641.2.3
by Monty Taylor
InnoDB Plugin 1.0.4 |
658 |
trx_id_t version_trx_id; |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
659 |
|
660 |
version_trx_id = row_get_rec_trx_id(version, index, *offsets); |
|
661 |
if (rec == version) { |
|
662 |
rec_trx_id = version_trx_id; |
|
663 |
}
|
|
664 |
||
665 |
mutex_enter(&kernel_mutex); |
|
666 |
version_trx = trx_get_on_id(version_trx_id); |
|
667 |
mutex_exit(&kernel_mutex); |
|
668 |
||
669 |
if (!version_trx |
|
670 |
|| version_trx->conc_state == TRX_NOT_STARTED |
|
671 |
|| version_trx->conc_state == TRX_COMMITTED_IN_MEMORY) { |
|
672 |
||
673 |
/* We found a version that belongs to a
|
|
674 |
committed transaction: return it. */
|
|
675 |
||
676 |
if (rec == version) { |
|
677 |
*old_vers = rec; |
|
678 |
err = DB_SUCCESS; |
|
679 |
break; |
|
680 |
}
|
|
681 |
||
682 |
/* We assume that a rolled-back transaction stays in
|
|
683 |
TRX_ACTIVE state until all the changes have been
|
|
684 |
rolled back and the transaction is removed from
|
|
685 |
the global list of transactions. */
|
|
686 |
||
1819.9.31
by Marko Mäkelä, Stewart Smith
Merge Revision revid:marko.makela@oracle.com-20100623110659-pk5bqnmo0j7hj6md from MySQL InnoDB |
687 |
if (rec_trx_id == version_trx_id) { |
641.1.2
by Monty Taylor
Imported 1.0.1 with clean - with no changes. |
688 |
/* The transaction was committed while
|
689 |
we searched for earlier versions.
|
|
690 |
Return the current version as a
|
|
691 |
semi-consistent read. */
|
|
692 |
||
693 |
version = rec; |
|
694 |
*offsets = rec_get_offsets(version, |
|
695 |
index, *offsets, |
|
696 |
ULINT_UNDEFINED, |
|
697 |
offset_heap); |
|
698 |
}
|
|
699 |
||
700 |
buf = mem_heap_alloc(in_heap, rec_offs_size(*offsets)); |
|
701 |
*old_vers = rec_copy(buf, version, *offsets); |
|
702 |
rec_offs_make_valid(*old_vers, index, *offsets); |
|
703 |
err = DB_SUCCESS; |
|
704 |
||
705 |
break; |
|
706 |
}
|
|
707 |
||
708 |
heap2 = heap; |
|
709 |
heap = mem_heap_create(1024); |
|
710 |
||
711 |
err = trx_undo_prev_version_build(rec, mtr, version, index, |
|
712 |
*offsets, heap, |
|
713 |
&prev_version); |
|
714 |
if (heap2) { |
|
715 |
mem_heap_free(heap2); /* free version */ |
|
716 |
}
|
|
717 |
||
718 |
if (UNIV_UNLIKELY(err != DB_SUCCESS)) { |
|
719 |
break; |
|
720 |
}
|
|
721 |
||
722 |
if (prev_version == NULL) { |
|
723 |
/* It was a freshly inserted version */
|
|
724 |
*old_vers = NULL; |
|
725 |
err = DB_SUCCESS; |
|
726 |
||
727 |
break; |
|
728 |
}
|
|
729 |
||
730 |
version = prev_version; |
|
731 |
*offsets = rec_get_offsets(version, index, *offsets, |
|
732 |
ULINT_UNDEFINED, offset_heap); |
|
733 |
}/* for (;;) */ |
|
734 |
||
735 |
if (heap) { |
|
736 |
mem_heap_free(heap); |
|
737 |
}
|
|
738 |
rw_lock_s_unlock(&(purge_sys->latch)); |
|
739 |
||
740 |
return(err); |
|
741 |
}
|