42
42
#include "ibuf0ibuf.h"
43
43
#include "trx0trx.h"
45
#ifdef UNIV_BLOB_DEBUG
49
/** TRUE when messages about index->blobs modification are enabled. */
50
static ibool btr_blob_dbg_msg;
52
/** Issue a message about an operation on index->blobs.
54
@param b the entry being subjected to the operation
55
@param ctx the context of the operation */
56
#define btr_blob_dbg_msg_issue(op, b, ctx) \
57
fprintf(stderr, op " %u:%u:%u->%u %s(%u,%u,%u)\n", \
58
(b)->ref_page_no, (b)->ref_heap_no, \
59
(b)->ref_field_no, (b)->blob_page_no, ctx, \
60
(b)->owner, (b)->always_owner, (b)->del)
62
/** Insert to index->blobs a reference to an off-page column.
63
@param index the index tree
64
@param b the reference
65
@param ctx context (for logging) */
68
btr_blob_dbg_rbt_insert(
69
/*====================*/
70
dict_index_t* index, /*!< in/out: index tree */
71
const btr_blob_dbg_t* b, /*!< in: the reference */
72
const char* ctx) /*!< in: context (for logging) */
74
if (btr_blob_dbg_msg) {
75
btr_blob_dbg_msg_issue("insert", b, ctx);
77
mutex_enter(&index->blobs_mutex);
78
rbt_insert(index->blobs, b, b);
79
mutex_exit(&index->blobs_mutex);
82
/** Remove from index->blobs a reference to an off-page column.
83
@param index the index tree
84
@param b the reference
85
@param ctx context (for logging) */
88
btr_blob_dbg_rbt_delete(
89
/*====================*/
90
dict_index_t* index, /*!< in/out: index tree */
91
const btr_blob_dbg_t* b, /*!< in: the reference */
92
const char* ctx) /*!< in: context (for logging) */
94
if (btr_blob_dbg_msg) {
95
btr_blob_dbg_msg_issue("delete", b, ctx);
97
mutex_enter(&index->blobs_mutex);
98
ut_a(rbt_delete(index->blobs, b));
99
mutex_exit(&index->blobs_mutex);
102
/**************************************************************//**
103
Comparator for items (btr_blob_dbg_t) in index->blobs.
104
The key in index->blobs is (ref_page_no, ref_heap_no, ref_field_no).
105
@return negative, 0 or positive if *a<*b, *a=*b, *a>*b */
110
const void* a, /*!< in: first btr_blob_dbg_t to compare */
111
const void* b) /*!< in: second btr_blob_dbg_t to compare */
113
const btr_blob_dbg_t* aa = a;
114
const btr_blob_dbg_t* bb = b;
119
if (aa->ref_page_no != bb->ref_page_no) {
120
return(aa->ref_page_no < bb->ref_page_no ? -1 : 1);
122
if (aa->ref_heap_no != bb->ref_heap_no) {
123
return(aa->ref_heap_no < bb->ref_heap_no ? -1 : 1);
125
if (aa->ref_field_no != bb->ref_field_no) {
126
return(aa->ref_field_no < bb->ref_field_no ? -1 : 1);
131
/**************************************************************//**
132
Add a reference to an off-page column to the index->blobs map. */
135
btr_blob_dbg_add_blob(
136
/*==================*/
137
const rec_t* rec, /*!< in: clustered index record */
138
ulint field_no, /*!< in: off-page column number */
139
ulint page_no, /*!< in: start page of the column */
140
dict_index_t* index, /*!< in/out: index tree */
141
const char* ctx) /*!< in: context (for logging) */
144
const page_t* page = page_align(rec);
148
b.blob_page_no = page_no;
149
b.ref_page_no = page_get_page_no(page);
150
b.ref_heap_no = page_rec_get_heap_no(rec);
151
b.ref_field_no = field_no;
152
ut_a(b.ref_field_no >= index->n_uniq);
153
b.always_owner = b.owner = TRUE;
155
ut_a(!rec_get_deleted_flag(rec, page_is_comp(page)));
156
btr_blob_dbg_rbt_insert(index, &b, ctx);
159
/**************************************************************//**
160
Add to index->blobs any references to off-page columns from a record.
161
@return number of references added */
164
btr_blob_dbg_add_rec(
165
/*=================*/
166
const rec_t* rec, /*!< in: record */
167
dict_index_t* index, /*!< in/out: index */
168
const ulint* offsets,/*!< in: offsets */
169
const char* ctx) /*!< in: context (for logging) */
176
ut_ad(rec_offs_validate(rec, index, offsets));
178
if (!rec_offs_any_extern(offsets)) {
182
b.ref_page_no = page_get_page_no(page_align(rec));
183
b.ref_heap_no = page_rec_get_heap_no(rec);
184
del = (rec_get_deleted_flag(rec, rec_offs_comp(offsets)) != 0);
186
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
187
if (rec_offs_nth_extern(offsets, i)) {
189
const byte* field_ref = rec_get_nth_field(
190
rec, offsets, i, &len);
192
ut_a(len != UNIV_SQL_NULL);
193
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
194
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
196
if (!memcmp(field_ref, field_ref_zero,
197
BTR_EXTERN_FIELD_REF_SIZE)) {
198
/* the column has not been stored yet */
203
b.blob_page_no = mach_read_from_4(
204
field_ref + BTR_EXTERN_PAGE_NO);
205
ut_a(b.ref_field_no >= index->n_uniq);
206
b.always_owner = b.owner
207
= !(field_ref[BTR_EXTERN_LEN]
208
& BTR_EXTERN_OWNER_FLAG);
211
btr_blob_dbg_rbt_insert(index, &b, ctx);
219
/**************************************************************//**
220
Display the references to off-page columns.
221
This function is to be called from a debugger,
222
for example when a breakpoint on ut_dbg_assertion_failed is hit. */
227
const dict_index_t* index) /*!< in: index tree */
229
const ib_rbt_node_t* node;
235
/* We intentionally do not acquire index->blobs_mutex here.
236
This function is to be called from a debugger, and the caller
237
should make sure that the index->blobs_mutex is held. */
239
for (node = rbt_first(index->blobs);
240
node != NULL; node = rbt_next(index->blobs, node)) {
241
const btr_blob_dbg_t* b
242
= rbt_value(btr_blob_dbg_t, node);
243
fprintf(stderr, "%u:%u:%u->%u%s%s%s\n",
244
b->ref_page_no, b->ref_heap_no, b->ref_field_no,
246
b->owner ? "" : "(disowned)",
247
b->always_owner ? "" : "(has disowned)",
248
b->del ? "(deleted)" : "");
252
/**************************************************************//**
253
Remove from index->blobs any references to off-page columns from a record.
254
@return number of references removed */
257
btr_blob_dbg_remove_rec(
258
/*====================*/
259
const rec_t* rec, /*!< in: record */
260
dict_index_t* index, /*!< in/out: index */
261
const ulint* offsets,/*!< in: offsets */
262
const char* ctx) /*!< in: context (for logging) */
268
ut_ad(rec_offs_validate(rec, index, offsets));
270
if (!rec_offs_any_extern(offsets)) {
274
b.ref_page_no = page_get_page_no(page_align(rec));
275
b.ref_heap_no = page_rec_get_heap_no(rec);
277
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
278
if (rec_offs_nth_extern(offsets, i)) {
280
const byte* field_ref = rec_get_nth_field(
281
rec, offsets, i, &len);
283
ut_a(len != UNIV_SQL_NULL);
284
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
285
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
288
b.blob_page_no = mach_read_from_4(
289
field_ref + BTR_EXTERN_PAGE_NO);
291
switch (b.blob_page_no) {
293
/* The column has not been stored yet.
294
The BLOB pointer must be all zero.
295
There cannot be a BLOB starting at
296
page 0, because page 0 is reserved for
297
the tablespace header. */
298
ut_a(!memcmp(field_ref, field_ref_zero,
299
BTR_EXTERN_FIELD_REF_SIZE));
302
/* the column has been freed already */
306
btr_blob_dbg_rbt_delete(index, &b, ctx);
314
/**************************************************************//**
315
Check that there are no references to off-page columns from or to
316
the given page. Invoked when freeing or clearing a page.
317
@return TRUE when no orphan references exist */
320
btr_blob_dbg_is_empty(
321
/*==================*/
322
dict_index_t* index, /*!< in: index */
323
ulint page_no) /*!< in: page number */
325
const ib_rbt_node_t* node;
326
ibool success = TRUE;
332
mutex_enter(&index->blobs_mutex);
334
for (node = rbt_first(index->blobs);
335
node != NULL; node = rbt_next(index->blobs, node)) {
336
const btr_blob_dbg_t* b
337
= rbt_value(btr_blob_dbg_t, node);
339
if (b->ref_page_no != page_no && b->blob_page_no != page_no) {
344
"InnoDB: orphan BLOB ref%s%s%s %u:%u:%u->%u\n",
345
b->owner ? "" : "(disowned)",
346
b->always_owner ? "" : "(has disowned)",
347
b->del ? "(deleted)" : "",
348
b->ref_page_no, b->ref_heap_no, b->ref_field_no,
351
if (b->blob_page_no != page_no || b->owner || !b->del) {
356
mutex_exit(&index->blobs_mutex);
360
/**************************************************************//**
361
Count and process all references to off-page columns on a page.
362
@return number of references processed */
367
const page_t* page, /*!< in: B-tree leaf page */
368
const rec_t* rec, /*!< in: record to start from
369
(NULL to process the whole page) */
370
dict_index_t* index, /*!< in/out: index */
371
const char* ctx, /*!< in: context (for logging) */
372
const btr_blob_dbg_op_f op) /*!< in: operation on records */
375
mem_heap_t* heap = NULL;
376
ulint offsets_[REC_OFFS_NORMAL_SIZE];
377
ulint* offsets = offsets_;
378
rec_offs_init(offsets_);
380
ut_a(fil_page_get_type(page) == FIL_PAGE_INDEX);
381
ut_a(!rec || page_align(rec) == page);
383
if (!index->blobs || !page_is_leaf(page)
384
|| !dict_index_is_clust(index)) {
389
rec = page_get_infimum_rec(page);
393
offsets = rec_get_offsets(rec, index, offsets,
394
ULINT_UNDEFINED, &heap);
395
count += op(rec, index, offsets, ctx);
396
rec = page_rec_get_next_const(rec);
397
} while (!page_rec_is_supremum(rec));
399
if (UNIV_LIKELY_NULL(heap)) {
406
/**************************************************************//**
407
Count and add to index->blobs any references to off-page columns
408
from records on a page.
409
@return number of references added */
414
const page_t* page, /*!< in: rewritten page */
415
dict_index_t* index, /*!< in/out: index */
416
const char* ctx) /*!< in: context (for logging) */
418
btr_blob_dbg_assert_empty(index, page_get_page_no(page));
420
return(btr_blob_dbg_op(page, NULL, index, ctx, btr_blob_dbg_add_rec));
423
/**************************************************************//**
424
Count and remove from index->blobs any references to off-page columns
425
from records on a page.
426
Used when reorganizing a page, before copying the records.
427
@return number of references removed */
432
const page_t* page, /*!< in: b-tree page */
433
dict_index_t* index, /*!< in/out: index */
434
const char* ctx) /*!< in: context (for logging) */
438
count = btr_blob_dbg_op(page, NULL, index, ctx,
439
btr_blob_dbg_remove_rec);
441
/* Check that no references exist. */
442
btr_blob_dbg_assert_empty(index, page_get_page_no(page));
447
/**************************************************************//**
448
Restore in index->blobs any references to off-page columns
449
Used when page reorganize fails due to compressed page overflow. */
452
btr_blob_dbg_restore(
453
/*=================*/
454
const page_t* npage, /*!< in: page that failed to compress */
455
const page_t* page, /*!< in: copy of original page */
456
dict_index_t* index, /*!< in/out: index */
457
const char* ctx) /*!< in: context (for logging) */
462
ut_a(page_get_page_no(npage) == page_get_page_no(page));
463
ut_a(page_get_space_id(npage) == page_get_space_id(page));
465
removed = btr_blob_dbg_remove(npage, index, ctx);
466
added = btr_blob_dbg_add(page, index, ctx);
467
ut_a(added == removed);
470
/**************************************************************//**
471
Modify the 'deleted' flag of a record. */
474
btr_blob_dbg_set_deleted_flag(
475
/*==========================*/
476
const rec_t* rec, /*!< in: record */
477
dict_index_t* index, /*!< in/out: index */
478
const ulint* offsets,/*!< in: rec_get_offs(rec, index) */
479
ibool del) /*!< in: TRUE=deleted, FALSE=exists */
481
const ib_rbt_node_t* node;
486
ut_ad(rec_offs_validate(rec, index, offsets));
487
ut_a(dict_index_is_clust(index));
488
ut_a(del == !!del);/* must be FALSE==0 or TRUE==1 */
490
if (!rec_offs_any_extern(offsets) || !index->blobs) {
495
b.ref_page_no = page_get_page_no(page_align(rec));
496
b.ref_heap_no = page_rec_get_heap_no(rec);
498
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
499
if (rec_offs_nth_extern(offsets, i)) {
501
const byte* field_ref = rec_get_nth_field(
502
rec, offsets, i, &len);
504
ut_a(len != UNIV_SQL_NULL);
505
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
506
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
509
b.blob_page_no = mach_read_from_4(
510
field_ref + BTR_EXTERN_PAGE_NO);
512
switch (b.blob_page_no) {
514
ut_a(memcmp(field_ref, field_ref_zero,
515
BTR_EXTERN_FIELD_REF_SIZE));
516
/* page number 0 is for the
517
page allocation bitmap */
519
/* the column has been freed already */
523
mutex_enter(&index->blobs_mutex);
524
node = rbt_lookup(index->blobs, &b);
527
c = rbt_value(btr_blob_dbg_t, node);
528
/* The flag should be modified. */
530
if (btr_blob_dbg_msg) {
532
mutex_exit(&index->blobs_mutex);
533
btr_blob_dbg_msg_issue("del_mk", &b, "");
535
mutex_exit(&index->blobs_mutex);
541
/**************************************************************//**
542
Change the ownership of an off-page column. */
547
const rec_t* rec, /*!< in: record */
548
dict_index_t* index, /*!< in/out: index */
549
const ulint* offsets,/*!< in: rec_get_offs(rec, index) */
550
ulint i, /*!< in: ith field in rec */
551
ibool own) /*!< in: TRUE=owned, FALSE=disowned */
553
const ib_rbt_node_t* node;
555
const byte* field_ref;
558
ut_ad(rec_offs_validate(rec, index, offsets));
559
ut_a(rec_offs_nth_extern(offsets, i));
561
field_ref = rec_get_nth_field(rec, offsets, i, &len);
562
ut_a(len != UNIV_SQL_NULL);
563
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
564
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
566
b.ref_page_no = page_get_page_no(page_align(rec));
567
b.ref_heap_no = page_rec_get_heap_no(rec);
569
b.owner = !(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG);
570
b.blob_page_no = mach_read_from_4(field_ref + BTR_EXTERN_PAGE_NO);
572
ut_a(b.owner == own);
574
mutex_enter(&index->blobs_mutex);
575
node = rbt_lookup(index->blobs, &b);
576
/* row_ins_clust_index_entry_by_modify() invokes
577
btr_cur_unmark_extern_fields() also for the newly inserted
578
references, which are all zero bytes until the columns are stored.
579
The node lookup must fail if and only if that is the case. */
580
ut_a(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)
584
btr_blob_dbg_t* c = rbt_value(btr_blob_dbg_t, node);
585
/* Some code sets ownership from TRUE to TRUE.
586
We do not allow changing ownership from FALSE to FALSE. */
587
ut_a(own || c->owner);
591
c->always_owner = FALSE;
595
mutex_exit(&index->blobs_mutex);
597
#endif /* UNIV_BLOB_DEBUG */
46
600
Latching strategy of the InnoDB B-tree
47
601
--------------------------------------