BLOB instrumentation for Bug#12612184 Race condition in row_upd_clust_rec()
If UNIV_DEBUG or UNIV_BLOB_LIGHT_DEBUG is enabled, add !rec_offs_any_null_extern() assertions, ensuring that records do not contain null pointers to externally stored columns in inappropriate places. btr_cur_optimistic_update(): Assert !rec_offs_any_null_extern(). Incomplete records must never be updated or deleted. This assertion will cover also the pessimistic route. row_build(): Assert !rec_offs_any_null_extern(). Search tuples must never be built from incomplete index entries. row_rec_to_index_entry(): Assert !rec_offs_any_null_extern() unless ROW_COPY_DATA is requested. ROW_COPY_DATA is used for multi-versioning, and therefore it might be valid to copy the most recent (uncommitted) version while it contains a null pointer to off-page columns. row_vers_build_for_consistent_read(), row_vers_build_for_semi_consistent_read(): Assert !rec_offs_any_null_extern() on all versions except the most recent one. trx_undo_prev_version_build(): Assert !rec_offs_any_null_extern() on the previous version. rb:682 approved by Sunny Bains
This commit is contained in:
parent
55015ea0b0
commit
6d20340c72
@ -73,6 +73,13 @@ this many index pages */
|
||||
+ not_empty) \
|
||||
/ (BTR_KEY_VAL_ESTIMATE_N_PAGES + ext_size))
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
/* A BLOB field reference full of zero, for use in assertions and tests.
|
||||
Initially, BLOB field references are set to zero, in
|
||||
dtuple_convert_big_rec(). */
|
||||
const byte field_ref_zero[BTR_EXTERN_FIELD_REF_SIZE];
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
/***********************************************************************
|
||||
Marks all extern fields in a record as owned by the record. This function
|
||||
should be called if the delete mark of a record is removed: a not delete
|
||||
@ -1585,6 +1592,9 @@ btr_cur_optimistic_update(
|
||||
|
||||
heap = mem_heap_create(1024);
|
||||
offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
if (btr_cur_print_record_ops && thr) {
|
||||
|
@ -18,4 +18,9 @@ typedef struct btr_pcur_struct btr_pcur_t;
|
||||
typedef struct btr_cur_struct btr_cur_t;
|
||||
typedef struct btr_search_struct btr_search_t;
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
#define BTR_EXTERN_FIELD_REF_SIZE 20
|
||||
extern const byte field_ref_zero[BTR_EXTERN_FIELD_REF_SIZE];
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
#endif
|
||||
|
@ -339,6 +339,19 @@ rec_offs_any_extern(
|
||||
/*================*/
|
||||
/* out: TRUE if a field is stored externally */
|
||||
const ulint* offsets);/* in: array returned by rec_get_offsets() */
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
/********************************************************
|
||||
Determine if the offsets are for a record containing null BLOB pointers. */
|
||||
UNIV_INLINE
|
||||
const byte*
|
||||
rec_offs_any_null_extern(
|
||||
/*=====================*/
|
||||
/* out: first field containing
|
||||
a null BLOB pointer,
|
||||
or NULL if none found */
|
||||
rec_t* rec, /*!< in: record */
|
||||
const ulint* offsets); /*!< in: rec_get_offsets(rec) */
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
/***************************************************************
|
||||
Sets the value of the ith field extern storage bit. */
|
||||
UNIV_INLINE
|
||||
|
@ -9,6 +9,7 @@ Created 5/30/1994 Heikki Tuuri
|
||||
#include "mach0data.h"
|
||||
#include "ut0byte.h"
|
||||
#include "dict0dict.h"
|
||||
#include "btr0types.h"
|
||||
|
||||
/* Compact flag ORed to the extra size returned by rec_get_offsets() */
|
||||
#define REC_OFFS_COMPACT ((ulint) 1 << 31)
|
||||
@ -1020,6 +1021,42 @@ rec_offs_any_extern(
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
/********************************************************
|
||||
Determine if the offsets are for a record containing null BLOB pointers. */
|
||||
UNIV_INLINE
|
||||
const byte*
|
||||
rec_offs_any_null_extern(
|
||||
/*=====================*/
|
||||
/* out: first field containing
|
||||
a null BLOB pointer,
|
||||
or NULL if none found */
|
||||
rec_t* rec, /*!< in: record */
|
||||
const ulint* offsets) /*!< in: rec_get_offsets(rec) */
|
||||
{
|
||||
ulint i;
|
||||
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
||||
|
||||
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
|
||||
if (rec_offs_nth_extern(offsets, i)) {
|
||||
ulint len;
|
||||
const byte* field
|
||||
= rec_get_nth_field(rec, offsets, i, &len);
|
||||
|
||||
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
|
||||
if (!memcmp(field + len
|
||||
- BTR_EXTERN_FIELD_REF_SIZE,
|
||||
field_ref_zero,
|
||||
BTR_EXTERN_FIELD_REF_SIZE)) {
|
||||
return(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
/***************************************************************
|
||||
Sets the value of the ith field extern storage bit. */
|
||||
UNIV_INLINE
|
||||
|
@ -88,6 +88,8 @@ memory is read outside the allocated blocks. */
|
||||
#if 0
|
||||
#define UNIV_DEBUG_VALGRIND /* Enable extra
|
||||
Valgrind instrumentation */
|
||||
#define UNIV_BLOB_LIGHT_DEBUG /* Enable off-page column
|
||||
debugging without UNIV_DEBUG */
|
||||
#define UNIV_DEBUG /* Enable ut_ad() assertions */
|
||||
#define UNIV_LIST_DEBUG /* debug UT_LIST_ macros */
|
||||
#define UNIV_MEM_DEBUG /* detect memory leaks etc */
|
||||
|
@ -210,6 +210,10 @@ row_build(
|
||||
ut_ad(rec_offs_validate(rec, index, offsets));
|
||||
}
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (type != ROW_COPY_POINTERS) {
|
||||
/* Take a copy of rec to heap */
|
||||
buf = mem_heap_alloc(heap, rec_offs_size(offsets));
|
||||
@ -302,6 +306,10 @@ row_rec_to_index_entry(
|
||||
rec = rec_copy(buf, rec, offsets);
|
||||
/* Avoid a debug assertion in rec_offs_validate(). */
|
||||
rec_offs_make_valid(rec, index, offsets);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
} else {
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
}
|
||||
|
||||
rec_len = rec_offs_n_fields(offsets);
|
||||
|
@ -473,6 +473,11 @@ row_vers_build_for_consistent_read(
|
||||
/* The view already sees this version: we can
|
||||
copy it to in_heap and return */
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(
|
||||
version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
buf = mem_heap_alloc(in_heap,
|
||||
rec_offs_size(*offsets));
|
||||
*old_vers = rec_copy(buf, version, *offsets);
|
||||
@ -506,6 +511,10 @@ row_vers_build_for_consistent_read(
|
||||
*offsets = rec_get_offsets(prev_version, index, *offsets,
|
||||
ULINT_UNDEFINED, offset_heap);
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(prev_version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
trx_id = row_get_rec_trx_id(prev_version, index, *offsets);
|
||||
|
||||
if (read_view_sees_trx_id(view, trx_id)) {
|
||||
@ -606,6 +615,10 @@ row_vers_build_for_semi_consistent_read(
|
||||
/* We found a version that belongs to a
|
||||
committed transaction: return it. */
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (rec == version) {
|
||||
*old_vers = rec;
|
||||
err = DB_SUCCESS;
|
||||
@ -663,6 +676,9 @@ row_vers_build_for_semi_consistent_read(
|
||||
version = prev_version;
|
||||
*offsets = rec_get_offsets(version, index, *offsets,
|
||||
ULINT_UNDEFINED, offset_heap);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
}/* for (;;) */
|
||||
|
||||
if (heap) {
|
||||
|
@ -1397,6 +1397,10 @@ trx_undo_prev_version_build(
|
||||
return(DB_ERROR);
|
||||
}
|
||||
|
||||
# if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
# endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (row_upd_changes_field_size_or_external(index, offsets, update)) {
|
||||
ulint* ext_vect;
|
||||
ulint n_ext_vect;
|
||||
|
@ -1,3 +1,8 @@
|
||||
2011-06-09 The InnoDB Team
|
||||
* btr/btr0cur.c, include/rem0rec.h, include/rem0rec.ic,
|
||||
* row/row0row.c, row/row0vers.c, trx/trx0rec.c:
|
||||
Instrumentation for Bug#12612184 Race condition in row_upd_clust_rec()
|
||||
|
||||
2011-04-07 The InnoDB Team
|
||||
|
||||
* handler/ha_innodb.cc, handler/ha_innodb.h, handler/handler0alter.cc:
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1994, 2010, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1994, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2008, Google Inc.
|
||||
|
||||
Portions of this file contain modifications contributed and copyrighted by
|
||||
@ -1871,6 +1871,9 @@ btr_cur_optimistic_update(
|
||||
|
||||
heap = mem_heap_create(1024);
|
||||
offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &heap);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
#ifdef UNIV_DEBUG
|
||||
if (btr_cur_print_record_ops && thr) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1994, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -480,6 +480,18 @@ ulint
|
||||
rec_offs_any_extern(
|
||||
/*================*/
|
||||
const ulint* offsets);/*!< in: array returned by rec_get_offsets() */
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
/******************************************************//**
|
||||
Determine if the offsets are for a record containing null BLOB pointers.
|
||||
@return first field containing a null BLOB pointer, or NULL if none found */
|
||||
UNIV_INLINE
|
||||
const byte*
|
||||
rec_offs_any_null_extern(
|
||||
/*=====================*/
|
||||
const rec_t* rec, /*!< in: record */
|
||||
const ulint* offsets) /*!< in: rec_get_offsets(rec) */
|
||||
__attribute__((nonnull, warn_unused_result));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
/******************************************************//**
|
||||
Returns nonzero if the extern bit is set in nth field of rec.
|
||||
@return nonzero if externally stored */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1994, 2009, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1994, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -26,6 +26,7 @@ Created 5/30/1994 Heikki Tuuri
|
||||
#include "mach0data.h"
|
||||
#include "ut0byte.h"
|
||||
#include "dict0dict.h"
|
||||
#include "btr0types.h"
|
||||
|
||||
/* Compact flag ORed to the extra size returned by rec_get_offsets() */
|
||||
#define REC_OFFS_COMPACT ((ulint) 1 << 31)
|
||||
@ -1087,6 +1088,44 @@ rec_offs_any_extern(
|
||||
return(UNIV_UNLIKELY(*rec_offs_base(offsets) & REC_OFFS_EXTERNAL));
|
||||
}
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
/******************************************************//**
|
||||
Determine if the offsets are for a record containing null BLOB pointers.
|
||||
@return first field containing a null BLOB pointer, or NULL if none found */
|
||||
UNIV_INLINE
|
||||
const byte*
|
||||
rec_offs_any_null_extern(
|
||||
/*=====================*/
|
||||
const rec_t* rec, /*!< in: record */
|
||||
const ulint* offsets) /*!< in: rec_get_offsets(rec) */
|
||||
{
|
||||
ulint i;
|
||||
ut_ad(rec_offs_validate(rec, NULL, offsets));
|
||||
|
||||
if (!rec_offs_any_extern(offsets)) {
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < rec_offs_n_fields(offsets); i++) {
|
||||
if (rec_offs_nth_extern(offsets, i)) {
|
||||
ulint len;
|
||||
const byte* field
|
||||
= rec_get_nth_field(rec, offsets, i, &len);
|
||||
|
||||
ut_a(len >= BTR_EXTERN_FIELD_REF_SIZE);
|
||||
if (!memcmp(field + len
|
||||
- BTR_EXTERN_FIELD_REF_SIZE,
|
||||
field_ref_zero,
|
||||
BTR_EXTERN_FIELD_REF_SIZE)) {
|
||||
return(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
/******************************************************//**
|
||||
Returns nonzero if the extern bit is set in nth field of rec.
|
||||
@return nonzero if externally stored */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -231,6 +231,10 @@ row_build(
|
||||
ut_ad(rec_offs_validate(rec, index, offsets));
|
||||
}
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (type != ROW_COPY_POINTERS) {
|
||||
/* Take a copy of rec to heap */
|
||||
buf = mem_heap_alloc(heap, rec_offs_size(offsets));
|
||||
@ -415,6 +419,10 @@ row_rec_to_index_entry(
|
||||
rec = rec_copy(buf, rec, offsets);
|
||||
/* Avoid a debug assertion in rec_offs_validate(). */
|
||||
rec_offs_make_valid(rec, index, offsets);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
} else {
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
}
|
||||
|
||||
entry = row_rec_to_index_entry_low(rec, index, offsets, n_ext, heap);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -550,6 +550,11 @@ row_vers_build_for_consistent_read(
|
||||
/* The view already sees this version: we can
|
||||
copy it to in_heap and return */
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(
|
||||
version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
buf = mem_heap_alloc(in_heap,
|
||||
rec_offs_size(*offsets));
|
||||
*old_vers = rec_copy(buf, version, *offsets);
|
||||
@ -583,6 +588,10 @@ row_vers_build_for_consistent_read(
|
||||
*offsets = rec_get_offsets(prev_version, index, *offsets,
|
||||
ULINT_UNDEFINED, offset_heap);
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(prev_version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
trx_id = row_get_rec_trx_id(prev_version, index, *offsets);
|
||||
|
||||
if (read_view_sees_trx_id(view, trx_id)) {
|
||||
@ -682,6 +691,10 @@ row_vers_build_for_semi_consistent_read(
|
||||
/* We found a version that belongs to a
|
||||
committed transaction: return it. */
|
||||
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (rec == version) {
|
||||
*old_vers = rec;
|
||||
err = DB_SUCCESS;
|
||||
@ -739,6 +752,9 @@ row_vers_build_for_semi_consistent_read(
|
||||
version = prev_version;
|
||||
*offsets = rec_get_offsets(version, index, *offsets,
|
||||
ULINT_UNDEFINED, offset_heap);
|
||||
#if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(version, *offsets));
|
||||
#endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
}/* for (;;) */
|
||||
|
||||
if (heap) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2010, Innobase Oy. All Rights Reserved.
|
||||
Copyright (c) 1996, 2011, Oracle and/or its affiliates. All Rights Reserved.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
@ -1577,6 +1577,10 @@ trx_undo_prev_version_build(
|
||||
return(DB_ERROR);
|
||||
}
|
||||
|
||||
# if defined UNIV_DEBUG || defined UNIV_BLOB_LIGHT_DEBUG
|
||||
ut_a(!rec_offs_any_null_extern(rec, offsets));
|
||||
# endif /* UNIV_DEBUG || UNIV_BLOB_LIGHT_DEBUG */
|
||||
|
||||
if (row_upd_changes_field_size_or_external(index, offsets, update)) {
|
||||
ulint n_ext;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user