43
43
#include "trx0trx.h"
44
44
#include "page0zip.h"
46
#ifdef UNIV_BLOB_DEBUG
50
/** TRUE when messages about index->blobs modification are enabled. */
51
static ibool btr_blob_dbg_msg;
53
/** Issue a message about an operation on index->blobs.
55
@param b the entry being subjected to the operation
56
@param ctx the context of the operation */
57
#define btr_blob_dbg_msg_issue(op, b, ctx) \
58
fprintf(stderr, op " %u:%u:%u->%u %s(%u,%u,%u)\n", \
59
(b)->ref_page_no, (b)->ref_heap_no, \
60
(b)->ref_field_no, (b)->blob_page_no, ctx, \
61
(b)->owner, (b)->always_owner, (b)->del)
63
/** Insert to index->blobs a reference to an off-page column.
64
@param index the index tree
65
@param b the reference
66
@param ctx context (for logging) */
69
btr_blob_dbg_rbt_insert(
70
/*====================*/
71
dict_index_t* index, /*!< in/out: index tree */
72
const btr_blob_dbg_t* b, /*!< in: the reference */
73
const char* ctx) /*!< in: context (for logging) */
75
if (btr_blob_dbg_msg) {
76
btr_blob_dbg_msg_issue("insert", b, ctx);
78
mutex_enter(&index->blobs_mutex);
79
rbt_insert(index->blobs, b, b);
80
mutex_exit(&index->blobs_mutex);
83
/** Remove from index->blobs a reference to an off-page column.
84
@param index the index tree
85
@param b the reference
86
@param ctx context (for logging) */
89
btr_blob_dbg_rbt_delete(
90
/*====================*/
91
dict_index_t* index, /*!< in/out: index tree */
92
const btr_blob_dbg_t* b, /*!< in: the reference */
93
const char* ctx) /*!< in: context (for logging) */
95
if (btr_blob_dbg_msg) {
96
btr_blob_dbg_msg_issue("delete", b, ctx);
98
mutex_enter(&index->blobs_mutex);
99
ut_a(rbt_delete(index->blobs, b));
100
mutex_exit(&index->blobs_mutex);
103
/**************************************************************//**
104
Comparator for items (btr_blob_dbg_t) in index->blobs.
105
The key in index->blobs is (ref_page_no, ref_heap_no, ref_field_no).
106
@return negative, 0 or positive if *a<*b, *a=*b, *a>*b */
111
const void* a, /*!< in: first btr_blob_dbg_t to compare */
112
const void* b) /*!< in: second btr_blob_dbg_t to compare */
114
const btr_blob_dbg_t* aa = a;
115
const btr_blob_dbg_t* bb = b;
120
if (aa->ref_page_no != bb->ref_page_no) {
121
return(aa->ref_page_no < bb->ref_page_no ? -1 : 1);
123
if (aa->ref_heap_no != bb->ref_heap_no) {
124
return(aa->ref_heap_no < bb->ref_heap_no ? -1 : 1);
126
if (aa->ref_field_no != bb->ref_field_no) {
127
return(aa->ref_field_no < bb->ref_field_no ? -1 : 1);
132
/**************************************************************//**
133
Add a reference to an off-page column to the index->blobs map. */
136
btr_blob_dbg_add_blob(
137
/*==================*/
138
const rec_t* rec, /*!< in: clustered index record */
139
ulint field_no, /*!< in: off-page column number */
140
ulint page_no, /*!< in: start page of the column */
141
dict_index_t* index, /*!< in/out: index tree */
142
const char* ctx) /*!< in: context (for logging) */
145
const page_t* page = page_align(rec);
149
b.blob_page_no = page_no;
150
b.ref_page_no = page_get_page_no(page);
151
b.ref_heap_no = page_rec_get_heap_no(rec);
152
b.ref_field_no = field_no;
153
ut_a(b.ref_field_no >= index->n_uniq);
154
b.always_owner = b.owner = TRUE;
156
ut_a(!rec_get_deleted_flag(rec, page_is_comp(page)));
157
btr_blob_dbg_rbt_insert(index, &b, ctx);
160
/**************************************************************//**
161
Add to index->blobs any references to off-page columns from a record.
162
@return number of references added */
165
btr_blob_dbg_add_rec(
166
/*=================*/
167
const rec_t* rec, /*!< in: record */
168
dict_index_t* index, /*!< in/out: index */
169
const ulint* offsets,/*!< in: offsets */
170
const char* ctx) /*!< in: context (for logging) */
177
ut_ad(rec_offs_validate(rec, index, offsets));
179
if (!rec_offs_any_extern(offsets)) {
183
b.ref_page_no = page_get_page_no(page_align(rec));
184
b.ref_heap_no = page_rec_get_heap_no(rec);
185
del = (rec_get_deleted_flag(rec, rec_offs_comp(offsets)) != 0);
187
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
188
if (rec_offs_nth_extern(offsets, i)) {
190
const byte* field_ref = rec_get_nth_field(
191
rec, offsets, i, &len);
193
ut_a(len != UNIV_SQL_NULL);
194
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
195
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
197
if (!memcmp(field_ref, field_ref_zero,
198
BTR_EXTERN_FIELD_REF_SIZE)) {
199
/* the column has not been stored yet */
204
b.blob_page_no = mach_read_from_4(
205
field_ref + BTR_EXTERN_PAGE_NO);
206
ut_a(b.ref_field_no >= index->n_uniq);
207
b.always_owner = b.owner
208
= !(field_ref[BTR_EXTERN_LEN]
209
& BTR_EXTERN_OWNER_FLAG);
212
btr_blob_dbg_rbt_insert(index, &b, ctx);
220
/**************************************************************//**
221
Display the references to off-page columns.
222
This function is to be called from a debugger,
223
for example when a breakpoint on ut_dbg_assertion_failed is hit. */
228
const dict_index_t* index) /*!< in: index tree */
230
const ib_rbt_node_t* node;
236
/* We intentionally do not acquire index->blobs_mutex here.
237
This function is to be called from a debugger, and the caller
238
should make sure that the index->blobs_mutex is held. */
240
for (node = rbt_first(index->blobs);
241
node != NULL; node = rbt_next(index->blobs, node)) {
242
const btr_blob_dbg_t* b
243
= rbt_value(btr_blob_dbg_t, node);
244
fprintf(stderr, "%u:%u:%u->%u%s%s%s\n",
245
b->ref_page_no, b->ref_heap_no, b->ref_field_no,
247
b->owner ? "" : "(disowned)",
248
b->always_owner ? "" : "(has disowned)",
249
b->del ? "(deleted)" : "");
253
/**************************************************************//**
254
Remove from index->blobs any references to off-page columns from a record.
255
@return number of references removed */
258
btr_blob_dbg_remove_rec(
259
/*====================*/
260
const rec_t* rec, /*!< in: record */
261
dict_index_t* index, /*!< in/out: index */
262
const ulint* offsets,/*!< in: offsets */
263
const char* ctx) /*!< in: context (for logging) */
269
ut_ad(rec_offs_validate(rec, index, offsets));
271
if (!rec_offs_any_extern(offsets)) {
275
b.ref_page_no = page_get_page_no(page_align(rec));
276
b.ref_heap_no = page_rec_get_heap_no(rec);
278
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
279
if (rec_offs_nth_extern(offsets, i)) {
281
const byte* field_ref = rec_get_nth_field(
282
rec, offsets, i, &len);
284
ut_a(len != UNIV_SQL_NULL);
285
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
286
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
289
b.blob_page_no = mach_read_from_4(
290
field_ref + BTR_EXTERN_PAGE_NO);
292
switch (b.blob_page_no) {
294
/* The column has not been stored yet.
295
The BLOB pointer must be all zero.
296
There cannot be a BLOB starting at
297
page 0, because page 0 is reserved for
298
the tablespace header. */
299
ut_a(!memcmp(field_ref, field_ref_zero,
300
BTR_EXTERN_FIELD_REF_SIZE));
303
/* the column has been freed already */
307
btr_blob_dbg_rbt_delete(index, &b, ctx);
315
/**************************************************************//**
316
Check that there are no references to off-page columns from or to
317
the given page. Invoked when freeing or clearing a page.
318
@return TRUE when no orphan references exist */
321
btr_blob_dbg_is_empty(
322
/*==================*/
323
dict_index_t* index, /*!< in: index */
324
ulint page_no) /*!< in: page number */
326
const ib_rbt_node_t* node;
327
ibool success = TRUE;
333
mutex_enter(&index->blobs_mutex);
335
for (node = rbt_first(index->blobs);
336
node != NULL; node = rbt_next(index->blobs, node)) {
337
const btr_blob_dbg_t* b
338
= rbt_value(btr_blob_dbg_t, node);
340
if (b->ref_page_no != page_no && b->blob_page_no != page_no) {
345
"InnoDB: orphan BLOB ref%s%s%s %u:%u:%u->%u\n",
346
b->owner ? "" : "(disowned)",
347
b->always_owner ? "" : "(has disowned)",
348
b->del ? "(deleted)" : "",
349
b->ref_page_no, b->ref_heap_no, b->ref_field_no,
352
if (b->blob_page_no != page_no || b->owner || !b->del) {
357
mutex_exit(&index->blobs_mutex);
361
/**************************************************************//**
362
Count and process all references to off-page columns on a page.
363
@return number of references processed */
368
const page_t* page, /*!< in: B-tree leaf page */
369
const rec_t* rec, /*!< in: record to start from
370
(NULL to process the whole page) */
371
dict_index_t* index, /*!< in/out: index */
372
const char* ctx, /*!< in: context (for logging) */
373
const btr_blob_dbg_op_f op) /*!< in: operation on records */
376
mem_heap_t* heap = NULL;
377
ulint offsets_[REC_OFFS_NORMAL_SIZE];
378
ulint* offsets = offsets_;
379
rec_offs_init(offsets_);
381
ut_a(fil_page_get_type(page) == FIL_PAGE_INDEX);
382
ut_a(!rec || page_align(rec) == page);
384
if (!index->blobs || !page_is_leaf(page)
385
|| !dict_index_is_clust(index)) {
390
rec = page_get_infimum_rec(page);
394
offsets = rec_get_offsets(rec, index, offsets,
395
ULINT_UNDEFINED, &heap);
396
count += op(rec, index, offsets, ctx);
397
rec = page_rec_get_next_const(rec);
398
} while (!page_rec_is_supremum(rec));
400
if (UNIV_LIKELY_NULL(heap)) {
407
/**************************************************************//**
408
Count and add to index->blobs any references to off-page columns
409
from records on a page.
410
@return number of references added */
415
const page_t* page, /*!< in: rewritten page */
416
dict_index_t* index, /*!< in/out: index */
417
const char* ctx) /*!< in: context (for logging) */
419
btr_blob_dbg_assert_empty(index, page_get_page_no(page));
421
return(btr_blob_dbg_op(page, NULL, index, ctx, btr_blob_dbg_add_rec));
424
/**************************************************************//**
425
Count and remove from index->blobs any references to off-page columns
426
from records on a page.
427
Used when reorganizing a page, before copying the records.
428
@return number of references removed */
433
const page_t* page, /*!< in: b-tree page */
434
dict_index_t* index, /*!< in/out: index */
435
const char* ctx) /*!< in: context (for logging) */
439
count = btr_blob_dbg_op(page, NULL, index, ctx,
440
btr_blob_dbg_remove_rec);
442
/* Check that no references exist. */
443
btr_blob_dbg_assert_empty(index, page_get_page_no(page));
448
/**************************************************************//**
449
Restore in index->blobs any references to off-page columns
450
Used when page reorganize fails due to compressed page overflow. */
453
btr_blob_dbg_restore(
454
/*=================*/
455
const page_t* npage, /*!< in: page that failed to compress */
456
const page_t* page, /*!< in: copy of original page */
457
dict_index_t* index, /*!< in/out: index */
458
const char* ctx) /*!< in: context (for logging) */
463
ut_a(page_get_page_no(npage) == page_get_page_no(page));
464
ut_a(page_get_space_id(npage) == page_get_space_id(page));
466
removed = btr_blob_dbg_remove(npage, index, ctx);
467
added = btr_blob_dbg_add(page, index, ctx);
468
ut_a(added == removed);
471
/**************************************************************//**
472
Modify the 'deleted' flag of a record. */
475
btr_blob_dbg_set_deleted_flag(
476
/*==========================*/
477
const rec_t* rec, /*!< in: record */
478
dict_index_t* index, /*!< in/out: index */
479
const ulint* offsets,/*!< in: rec_get_offs(rec, index) */
480
ibool del) /*!< in: TRUE=deleted, FALSE=exists */
482
const ib_rbt_node_t* node;
487
ut_ad(rec_offs_validate(rec, index, offsets));
488
ut_a(dict_index_is_clust(index));
489
ut_a(del == !!del);/* must be FALSE==0 or TRUE==1 */
491
if (!rec_offs_any_extern(offsets) || !index->blobs) {
496
b.ref_page_no = page_get_page_no(page_align(rec));
497
b.ref_heap_no = page_rec_get_heap_no(rec);
499
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
500
if (rec_offs_nth_extern(offsets, i)) {
502
const byte* field_ref = rec_get_nth_field(
503
rec, offsets, i, &len);
505
ut_a(len != UNIV_SQL_NULL);
506
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
507
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
510
b.blob_page_no = mach_read_from_4(
511
field_ref + BTR_EXTERN_PAGE_NO);
513
switch (b.blob_page_no) {
515
ut_a(memcmp(field_ref, field_ref_zero,
516
BTR_EXTERN_FIELD_REF_SIZE));
517
/* page number 0 is for the
518
page allocation bitmap */
520
/* the column has been freed already */
524
mutex_enter(&index->blobs_mutex);
525
node = rbt_lookup(index->blobs, &b);
528
c = rbt_value(btr_blob_dbg_t, node);
529
/* The flag should be modified. */
531
if (btr_blob_dbg_msg) {
533
mutex_exit(&index->blobs_mutex);
534
btr_blob_dbg_msg_issue("del_mk", &b, "");
536
mutex_exit(&index->blobs_mutex);
542
/**************************************************************//**
543
Change the ownership of an off-page column. */
548
const rec_t* rec, /*!< in: record */
549
dict_index_t* index, /*!< in/out: index */
550
const ulint* offsets,/*!< in: rec_get_offs(rec, index) */
551
ulint i, /*!< in: ith field in rec */
552
ibool own) /*!< in: TRUE=owned, FALSE=disowned */
554
const ib_rbt_node_t* node;
556
const byte* field_ref;
559
ut_ad(rec_offs_validate(rec, index, offsets));
560
ut_a(rec_offs_nth_extern(offsets, i));
562
field_ref = rec_get_nth_field(rec, offsets, i, &len);
563
ut_a(len != UNIV_SQL_NULL);
564
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
565
field_ref += len - BTR_EXTERN_FIELD_REF_SIZE;
567
b.ref_page_no = page_get_page_no(page_align(rec));
568
b.ref_heap_no = page_rec_get_heap_no(rec);
570
b.owner = !(field_ref[BTR_EXTERN_LEN] & BTR_EXTERN_OWNER_FLAG);
571
b.blob_page_no = mach_read_from_4(field_ref + BTR_EXTERN_PAGE_NO);
573
ut_a(b.owner == own);
575
mutex_enter(&index->blobs_mutex);
576
node = rbt_lookup(index->blobs, &b);
577
/* row_ins_clust_index_entry_by_modify() invokes
578
btr_cur_unmark_extern_fields() also for the newly inserted
579
references, which are all zero bytes until the columns are stored.
580
The node lookup must fail if and only if that is the case. */
581
ut_a(!memcmp(field_ref, field_ref_zero, BTR_EXTERN_FIELD_REF_SIZE)
585
btr_blob_dbg_t* c = rbt_value(btr_blob_dbg_t, node);
586
/* Some code sets ownership from TRUE to TRUE.
587
We do not allow changing ownership from FALSE to FALSE. */
588
ut_a(own || c->owner);
592
c->always_owner = FALSE;
596
mutex_exit(&index->blobs_mutex);
598
#endif /* UNIV_BLOB_DEBUG */
47
601
Latching strategy of the InnoDB B-tree
48
602
--------------------------------------