Commit 08ba3887 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-12353: Replace MLOG_REC_INSERT,MLOG_COMP_REC_INSERT

page_mem_alloc_free(), page_dir_set_n_heap(), page_ptr_set_direction():
Merge with the callers.

page_direction_reset(), page_direction_increment(),
page_zip_dir_insert(), page_zip_write_rec_ext(), page_zip_write_rec():
Add the parameter mtr, and write log.

PageBulk::insert(), PageBulk::finish(): Write log for all changes.

page_cur_rec_insert(), page_cur_insert_rec_write_log(),
page_cur_insert_rec_write_log(): Remove.

page_rec_set_next(), page_header_set_field(), page_header_set_ptr():
Remove. Use lower-level operations with or without logging.

page_zip_dir_add_slot(): Move to the same compilation unit with
its only caller, page_cur_insert_rec_zip().

page_cur_insert_rec_zip(): Mark pieces of code that must be skipped
once this task is completed.

btr_defragment_chunk(): Before starting a mini-transaction that
is writing (a lot), invoke log_free_check(). This should allow
the test innodb.innodb_defrag_concurrent to pass with the
mtr default_mysqld.cnf setting of innodb_log_file_size=10M.

MLOG_BUF_MARGIN: Remove.
parent 2c4d5aa0
--loose-innodb-sort-buffer-size=64k
--loose-innodb-online-alter-log-max-size=128k
--loose-innodb-buffer-pool-size=5M
--loose-innodb-log-buffer-size=256k
--loose-innodb-sys-indexes
--loose-innodb-sys-fields
--loose-innodb-buffer-pool-stats
--loose-innodb-buffer-page
--loose-innodb-buffer-page-lru
--innodb-log-buffer-size=2m
--innodb-log-buffer-size=6m
--innodb-defragment=1
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -770,6 +770,7 @@ static void btr_defragment_chunk(void*)
return;
}
}
log_free_check();
mtr_start(&mtr);
cursor = btr_pcur_get_btr_cur(pcur);
index = btr_cur_get_index(cursor);
......
......@@ -183,82 +183,6 @@ rtr_index_build_node_ptr(
return(tuple);
}
/**************************************************************//**
In-place update the mbr field of a spatial index row.
@return true if update is successful */
static
bool
rtr_update_mbr_field_in_place(
/*==========================*/
dict_index_t* index, /*!< in: spatial index. */
rec_t* rec, /*!< in/out: rec to be modified.*/
offset_t* offsets, /*!< in/out: offsets on rec. */
rtr_mbr_t* mbr, /*!< in: the new mbr. */
mtr_t* mtr) /*!< in: mtr */
{
void* new_mbr_ptr;
double new_mbr[SPDIMS * 2];
byte* log_ptr;
page_t* page = page_align(rec);
ulint len = DATA_MBR_LEN;
ulint flags = BTR_NO_UNDO_LOG_FLAG
| BTR_NO_LOCKING_FLAG
| BTR_KEEP_SYS_FLAG;
ulint rec_info;
rtr_write_mbr(reinterpret_cast<byte*>(&new_mbr), mbr);
new_mbr_ptr = static_cast<void*>(new_mbr);
/* Otherwise, set the mbr to the new_mbr. */
rec_set_nth_field(rec, offsets, 0, new_mbr_ptr, len);
rec_info = rec_get_info_bits(rec, rec_offs_comp(offsets));
/* Write redo log. */
/* For now, we use LOG_REC_UPDATE_IN_PLACE to log this enlarge.
In the future, we may need to add a new log type for this. */
log_ptr = mlog_open_and_write_index(mtr, rec, index, page_is_comp(page)
? MLOG_COMP_REC_UPDATE_IN_PLACE
: MLOG_REC_UPDATE_IN_PLACE,
1 + DATA_ROLL_PTR_LEN + 14 + 2
+ MLOG_BUF_MARGIN);
if (!log_ptr) {
/* Logging in mtr is switched off during
crash recovery */
return(false);
}
/* Flags */
mach_write_to_1(log_ptr, flags);
log_ptr++;
/* TRX_ID Position */
log_ptr += mach_write_compressed(log_ptr, 0);
/* ROLL_PTR */
trx_write_roll_ptr(log_ptr, 0);
log_ptr += DATA_ROLL_PTR_LEN;
/* TRX_ID */
log_ptr += mach_u64_write_compressed(log_ptr, 0);
/* Offset */
mach_write_to_2(log_ptr, page_offset(rec));
log_ptr += 2;
/* Info bits */
mach_write_to_1(log_ptr, rec_info);
log_ptr++;
/* N fields */
log_ptr += mach_write_compressed(log_ptr, 1);
/* Field no, len */
log_ptr += mach_write_compressed(log_ptr, 0);
log_ptr += mach_write_compressed(log_ptr, len);
/* Data */
memcpy(log_ptr, new_mbr_ptr, len);
log_ptr += len;
mlog_close(mtr, log_ptr);
return(true);
}
/**************************************************************//**
Update the mbr field of a spatial index row.
@return true if update is successful */
......@@ -280,7 +204,7 @@ rtr_update_mbr_field(
mem_heap_t* heap;
page_t* page;
rec_t* rec;
ulint flags = BTR_NO_UNDO_LOG_FLAG
constexpr ulint flags = BTR_NO_UNDO_LOG_FLAG
| BTR_NO_LOCKING_FLAG
| BTR_KEEP_SYS_FLAG;
dberr_t err;
......@@ -291,7 +215,6 @@ rtr_update_mbr_field(
ulint low_match = 0;
ulint child;
ulint rec_info;
page_zip_des_t* page_zip;
bool ins_suc = true;
ulint cur2_pos = 0;
ulint del_page_no = 0;
......@@ -305,7 +228,6 @@ rtr_update_mbr_field(
heap = mem_heap_create(100);
block = btr_cur_get_block(cursor);
ut_ad(page == buf_block_get_frame(block));
page_zip = buf_block_get_page_zip(block);
child = btr_node_ptr_get_child_page_no(rec, offsets);
const bool is_leaf = page_is_leaf(block->frame);
......@@ -330,11 +252,16 @@ rtr_update_mbr_field(
cur2_pos = page_rec_get_n_recs_before(btr_cur_get_rec(cursor2));
}
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(rec_offs_base(offsets)[0 + 1] == DATA_MBR_LEN);
ut_ad(node_ptr->fields[0].len == DATA_MBR_LEN);
if (rec_info & REC_INFO_MIN_REC_FLAG) {
/* When the rec is minimal rec in this level, we do
in-place update for avoiding it move to other place. */
in-place update for avoiding it move to other place. */
page_zip_des_t* page_zip = buf_block_get_page_zip(block);
if (page_zip) {
if (UNIV_LIKELY_NULL(page_zip)) {
/* Check if there's enough space for in-place
update the zip page. */
if (!btr_cur_update_alloc_zip(
......@@ -370,21 +297,18 @@ rtr_update_mbr_field(
rec, rec_offs_comp(offsets));
ut_ad(rec_info & REC_INFO_MIN_REC_FLAG);
#endif /* UNIV_DEBUG */
}
if (!rtr_update_mbr_field_in_place(index, rec,
offsets, mbr, mtr)) {
return(false);
}
if (page_zip) {
page_zip_write_rec(page_zip, rec, index, offsets, 0);
memcpy(rec, node_ptr->fields[0].data, DATA_MBR_LEN);
page_zip_write_rec(page_zip, rec, index, offsets, 0,
mtr);
} else {
mtr->memcpy(block, page_offset(rec),
node_ptr->fields[0].data, DATA_MBR_LEN);
}
if (cursor2) {
offset_t* offsets2;
if (page_zip) {
if (UNIV_LIKELY_NULL(page_zip)) {
cursor2->page_cur.rec
= page_rec_get_nth(page, cur2_pos);
}
......
......@@ -3833,15 +3833,8 @@ ibuf_insert_to_index_page(
/* This is the easy case. Do something similar
to btr_cur_update_in_place(). */
rec = page_cur_get_rec(&page_cur);
row_upd_rec_in_place(rec, index, offsets,
update, page_zip);
/* Log the update in place operation. During recovery
MLOG_COMP_REC_UPDATE_IN_PLACE/MLOG_REC_UPDATE_IN_PLACE
expects trx_id, roll_ptr for secondary indexes. So we
just write dummy trx_id(0), roll_ptr(0) */
btr_cur_update_in_place_log(BTR_KEEP_SYS_FLAG, rec,
index, update, 0, 0, mtr);
btr_cur_upd_rec_in_place(rec, index, offsets,
update, block, mtr);
DBUG_EXECUTE_IF(
"crash_after_log_ibuf_upd_inplace",
......
......@@ -356,6 +356,22 @@ btr_cur_update_alloc_zip_func(
# define btr_cur_update_alloc_zip(page_zip,cursor,index,offsets,len,cr,mtr) \
btr_cur_update_alloc_zip_func(page_zip,cursor,index,len,cr,mtr)
#endif /* UNIV_DEBUG */
/** Apply an update vector to a record. No field size changes are allowed.
This is usually invoked on a clustered index. The only use case for a
secondary index is row_ins_sec_index_entry_by_modify() or its
counterpart in ibuf_insert_to_index_page().
@param[in,out] rec index record
@param[in] index the index of the record
@param[in] offsets rec_get_offsets(rec, index)
@param[in] update update vector
@param[in,out] block index page
@param[in,out] mtr mini-transaction */
void btr_cur_upd_rec_in_place(rec_t *rec, const dict_index_t *index,
const offset_t *offsets, const upd_t *update,
buf_block_t *block, mtr_t *mtr)
MY_ATTRIBUTE((nonnull));
/*************************************************************//**
Updates a record when the update causes no size changes in its fields.
@return locking or undo log related error code, or
......@@ -380,19 +396,6 @@ btr_cur_update_in_place(
mtr_commit(mtr) before latching any
further pages */
MY_ATTRIBUTE((warn_unused_result, nonnull));
/***********************************************************//**
Writes a redo log record of updating a record in-place. */
void
btr_cur_update_in_place_log(
/*========================*/
ulint flags, /*!< in: flags */
const rec_t* rec, /*!< in: record */
dict_index_t* index, /*!< in: index of the record */
const upd_t* update, /*!< in: update vector */
trx_id_t trx_id, /*!< in: transaction id */
roll_ptr_t roll_ptr, /*!< in: roll ptr */
mtr_t* mtr) /*!< in: mtr */
MY_ATTRIBUTE((nonnull));
/*************************************************************//**
Tries to update a record on a page in an index tree. It is assumed that mtr
holds an x-latch on the page. The operation does not succeed if there is too
......@@ -563,7 +566,8 @@ btr_cur_parse_update_in_place(
const byte* end_ptr,/*!< in: buffer end */
page_t* page, /*!< in/out: page or NULL */
page_zip_des_t* page_zip,/*!< in/out: compressed page, or NULL */
dict_index_t* index); /*!< in: index corresponding to page */
dict_index_t* index, /*!< in: index corresponding to page */
mtr_t* mtr); /*!< in/out: mini-transaction */
/****************************************************************//**
Parses the redo log record for delete marking or unmarking of a clustered
index record.
......
/*****************************************************************************
Copyright (c) 2013, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2020, MariaDB Corporation.
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
......@@ -29,8 +30,7 @@ Created 2013-03-16 Sunny Bains
/** Value of dyn_block_t::magic_n */
#define DYN_BLOCK_MAGIC_N 375767
/** This is the initial 'payload' size of a dynamic array;
this must be > MLOG_BUF_MARGIN + 30! */
/** This is the initial 'payload' size of a dynamic array */
#define DYN_ARRAY_DATA_SIZE 512
/** Flag for dyn_block_t::used that indicates a full block */
......
......@@ -239,10 +239,6 @@ mlog_parse_index(
bool comp, /*!< in: TRUE=compact record format */
dict_index_t** index); /*!< out, own: dummy index */
/** Insert, update, and maybe other functions may use this value to define an
extra mlog buffer size for variable size data */
#define MLOG_BUF_MARGIN 256
#include "mtr0log.ic"
#endif /* mtr0log_h */
......@@ -150,29 +150,8 @@ page_cur_tuple_insert(
offset_t** offsets,/*!< out: offsets on *rec */
mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */
ulint n_ext, /*!< in: number of externally stored columns */
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
MY_ATTRIBUTE((nonnull(1,2,3,4,5), warn_unused_result));
/***********************************************************//**
Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
page_cur_rec_insert(
/*================*/
page_cur_t* cursor, /*!< in/out: a page cursor */
const rec_t* rec, /*!< in: record to insert */
dict_index_t* index, /*!< in: record descriptor */
offset_t* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr); /*!< in: mini-transaction handle, or NULL */
mtr_t* mtr) /*!< in/out: mini-transaction */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/***********************************************************//**
Inserts a record next to page cursor on an uncompressed page.
Returns pointer to inserted record if succeed, i.e., enough
......@@ -306,23 +285,10 @@ page_cur_open_on_rnd_user_rec(
/*==========================*/
buf_block_t* block, /*!< in: page */
page_cur_t* cursor);/*!< out: page cursor */
/** Write a redo log record of inserting a record into an index page.
@param[in] insert_rec inserted record
@param[in] rec_size rec_get_size(insert_rec)
@param[in] cursor_rec predecessor of insert_rec
@param[in,out] index index tree
@param[in,out] mtr mini-transaction */
void
page_cur_insert_rec_write_log(
const rec_t* insert_rec,
ulint rec_size,
const rec_t* cursor_rec,
dict_index_t* index,
mtr_t* mtr)
MY_ATTRIBUTE((nonnull));
/***********************************************************//**
Parses a log record of a record insert on a page.
@return end of log record or NULL */
ATTRIBUTE_COLD /* only used when crash-upgrading */
const byte*
page_cur_parse_insert_rec(
/*======================*/
......
/*****************************************************************************
Copyright (c) 1994, 2014, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2015, 2019, MariaDB Corporation.
Copyright (c) 2015, 2020, MariaDB Corporation.
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
......@@ -257,7 +257,7 @@ page_cur_tuple_insert(
offset_t** offsets,/*!< out: offsets on *rec */
mem_heap_t** heap, /*!< in/out: pointer to memory heap, or NULL */
ulint n_ext, /*!< in: number of externally stored columns */
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
mtr_t* mtr) /*!< in/out: mini-transaction */
{
rec_t* rec;
ulint size = rec_get_converted_size(index, tuple, n_ext);
......@@ -288,34 +288,3 @@ page_cur_tuple_insert(
ut_ad(!rec || !cmp_dtuple_rec(tuple, rec, *offsets));
return(rec);
}
/***********************************************************//**
Inserts a record next to page cursor. Returns pointer to inserted record if
succeed, i.e., enough space available, NULL otherwise. The cursor stays at
the same logical position, but the physical position may change if it is
pointing to a compressed page that was reorganized.
IMPORTANT: The caller will have to update IBUF_BITMAP_FREE
if this is a compressed leaf page in a secondary index.
This has to be done either within the same mini-transaction,
or by invoking ibuf_reset_free_bits() before mtr_commit().
@return pointer to record if succeed, NULL otherwise */
UNIV_INLINE
rec_t*
page_cur_rec_insert(
/*================*/
page_cur_t* cursor, /*!< in/out: a page cursor */
const rec_t* rec, /*!< in: record to insert */
dict_index_t* index, /*!< in: record descriptor */
offset_t* offsets,/*!< in/out: rec_get_offsets(rec, index) */
mtr_t* mtr) /*!< in: mini-transaction handle, or NULL */
{
if (is_buf_block_get_page_zip(cursor->block)) {
return(page_cur_insert_rec_zip(
cursor, index, rec, offsets, mtr));
} else {
return(page_cur_insert_rec_low(
cursor, index, rec, offsets, mtr));
}
}
......@@ -506,17 +506,6 @@ inline uint16_t page_header_get_field(const page_t *page, ulint field)
#ifndef UNIV_INNOCHECKSUM
/*************************************************************//**
Sets the given header field. */
UNIV_INLINE
void
page_header_set_field(
/*==================*/
page_t* page, /*!< in/out: page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL */
ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */
ulint val); /*!< in: value */
/*************************************************************//**
Returns the offset stored in the given header field.
@return offset from the start of the page, or 0 */
UNIV_INLINE
......@@ -532,17 +521,6 @@ Returns the pointer stored in the given header field, or NULL. */
#define page_header_get_ptr(page, field) \
(page_header_get_offs(page, field) \
? page + page_header_get_offs(page, field) : NULL)
/*************************************************************//**
Sets the pointer stored in the given header field. */
UNIV_INLINE
void
page_header_set_ptr(
/*================*/
page_t* page, /*!< in/out: page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL */
ulint field, /*!< in/out: PAGE_FREE, ... */
const byte* ptr); /*!< in: pointer or NULL*/
/**
Reset PAGE_LAST_INSERT.
......@@ -632,19 +610,6 @@ page_dir_get_n_heap(
/*================*/
const page_t* page); /*!< in: index page */
/*************************************************************//**
Sets the number of records in the heap. */
UNIV_INLINE
void
page_dir_set_n_heap(
/*================*/
page_t* page, /*!< in/out: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL.
Note that the size of the dense page directory
in the compressed page trailer is
n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
ulint n_heap);/*!< in: number of records */
/*************************************************************//**
Gets the number of dir slots in directory.
@return number of slots */
UNIV_INLINE
......@@ -797,16 +762,6 @@ page_rec_get_next_non_del_marked(
/*=============================*/
const rec_t* rec); /*!< in: pointer to record */
/************************************************************//**
Sets the pointer to the next record on the page. */
UNIV_INLINE
void
page_rec_set_next(
/*==============*/
rec_t* rec, /*!< in: pointer to record,
must not be page supremum */
const rec_t* next); /*!< in: pointer to next record,
must not be page infimum */
/************************************************************//**
Gets the pointer to the previous record.
@return pointer to previous record */
UNIV_INLINE
......@@ -941,21 +896,6 @@ uint16_t
page_get_data_size(
/*===============*/
const page_t* page); /*!< in: index page */
/************************************************************//**
Allocates a block of memory from the head of the free list
of an index page. */
UNIV_INLINE
void
page_mem_alloc_free(
/*================*/
page_t* page, /*!< in/out: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
space available for inserting the record,
or NULL */
rec_t* next_rec,/*!< in: pointer to the new head of the
free record list */
ulint need); /*!< in: number of bytes allocated */
/** Read the PAGE_DIRECTION field from a byte.
@param[in] ptr pointer to PAGE_DIRECTION_B
@return the value of the PAGE_DIRECTION field */
......@@ -963,13 +903,6 @@ inline
byte
page_ptr_get_direction(const byte* ptr);
/** Set the PAGE_DIRECTION field.
@param[in] ptr pointer to PAGE_DIRECTION_B
@param[in] dir the value of the PAGE_DIRECTION field */
inline
void
page_ptr_set_direction(byte* ptr, byte dir);
/** Read the PAGE_DIRECTION field.
@param[in] page index page
@return the value of the PAGE_DIRECTION field */
......
......@@ -105,33 +105,6 @@ page_set_ssn_id(
#endif /* !UNIV_INNOCHECKSUM */
#ifndef UNIV_INNOCHECKSUM
/*************************************************************//**
Sets the given header field. */
UNIV_INLINE
void
page_header_set_field(
/*==================*/
page_t* page, /*!< in/out: page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL */
ulint field, /*!< in: PAGE_N_DIR_SLOTS, ... */
ulint val) /*!< in: value */
{
ut_ad(page);
ut_ad(field <= PAGE_N_RECS);
#if 0 /* FIXME: MDEV-19344 hits this */
ut_ad(field != PAGE_N_RECS || val);
#endif
ut_ad(field == PAGE_N_HEAP || val < srv_page_size);
ut_ad(field != PAGE_N_HEAP || (val & 0x7fff) < srv_page_size);
mach_write_to_2(page + PAGE_HEADER + field, val);
if (page_zip) {
page_zip_write_header(page_zip,
page + PAGE_HEADER + field, 2, NULL);
}
}
/*************************************************************//**
Returns the offset stored in the given header field.
@return offset from the start of the page, or 0 */
......@@ -153,35 +126,6 @@ page_header_get_offs(
return(offs);
}
/*************************************************************//**
Sets the pointer stored in the given header field. */
UNIV_INLINE
void
page_header_set_ptr(
/*================*/
page_t* page, /*!< in: page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL */
ulint field, /*!< in: PAGE_FREE, ... */
const byte* ptr) /*!< in: pointer or NULL*/
{
ulint offs;
ut_ad(page);
ut_ad((field == PAGE_FREE)
|| (field == PAGE_LAST_INSERT)
|| (field == PAGE_HEAP_TOP));
if (ptr == NULL) {
offs = 0;
} else {
offs = ulint(ptr - page);
}
ut_ad((field != PAGE_HEAP_TOP) || offs);
page_header_set_field(page, page_zip, field, offs);
}
/**
Reset PAGE_LAST_INSERT.
......@@ -432,29 +376,6 @@ page_dir_get_n_heap(
return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
}
/*************************************************************//**
Sets the number of records in the heap. */
UNIV_INLINE
void
page_dir_set_n_heap(
/*================*/
page_t* page, /*!< in/out: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page whose
uncompressed part will be updated, or NULL.
Note that the size of the dense page directory
in the compressed page trailer is
n_heap * PAGE_ZIP_DIR_SLOT_SIZE. */
ulint n_heap) /*!< in: number of records */
{
ut_ad(n_heap < 0x8000);
ut_ad(!page_zip || uint16_t(n_heap)
== (page_header_get_field(page, PAGE_N_HEAP) & 0x7fff) + 1);
page_header_set_field(page, page_zip, PAGE_N_HEAP, n_heap
| (0x8000
& page_header_get_field(page, PAGE_N_HEAP)));
}
/**************************************************************//**
Used to check the consistency of a record on a page.
@return TRUE if succeed */
......@@ -595,35 +516,6 @@ page_rec_get_next_non_del_marked(
return(r);
}
/************************************************************//**
Sets the pointer to the next record on the page. */
UNIV_INLINE
void
page_rec_set_next(
/*==============*/
rec_t* rec, /*!< in: pointer to record,
must not be page supremum */
const rec_t* next) /*!< in: pointer to next record,
must not be page infimum */
{
ulint offs;
ut_ad(page_rec_check(rec));
ut_ad(!page_rec_is_supremum(rec));
ut_ad(rec != next);
ut_ad(!next || !page_rec_is_infimum(next));
ut_ad(!next || page_align(rec) == page_align(next));
offs = next != NULL ? page_offset(next) : 0;
if (page_rec_is_comp(rec)) {
rec_set_next_offs_new(rec, offs);
} else {
rec_set_next_offs_old(rec, offs);
}
}
/************************************************************//**
Gets the pointer to the previous record.
@return pointer to previous record */
......@@ -745,39 +637,6 @@ page_get_data_size(
}
#ifndef UNIV_INNOCHECKSUM
/************************************************************//**
Allocates a block of memory from the free list of an index page. */
UNIV_INLINE
void
page_mem_alloc_free(
/*================*/
page_t* page, /*!< in/out: index page */
page_zip_des_t* page_zip,/*!< in/out: compressed page with enough
space available for inserting the record,
or NULL */
rec_t* next_rec,/*!< in: pointer to the new head of the
free record list */
ulint need) /*!< in: number of bytes allocated */
{
ulint garbage;
#ifdef UNIV_DEBUG
const rec_t* old_rec = page_header_get_ptr(page, PAGE_FREE);
ulint next_offs;
ut_ad(old_rec);
next_offs = rec_get_next_offs(old_rec, page_is_comp(page));
ut_ad(next_rec == (next_offs ? page + next_offs : NULL));
#endif
page_header_set_ptr(page, page_zip, PAGE_FREE, next_rec);
garbage = page_header_get_field(page, PAGE_GARBAGE);
ut_ad(garbage >= need);
page_header_set_field(page, page_zip, PAGE_GARBAGE, garbage - need);
}
/*************************************************************//**
Calculates free space if a page is emptied.
@return free space */
......@@ -884,19 +743,6 @@ page_ptr_get_direction(const byte* ptr)
return *ptr & ((1U << 3) - 1);
}
/** Set the PAGE_DIRECTION field.
@param[in] ptr pointer to PAGE_DIRECTION_B
@param[in] dir the value of the PAGE_DIRECTION field */
inline
void
page_ptr_set_direction(byte* ptr, byte dir)
{
ut_ad(page_offset(ptr) == PAGE_HEADER + PAGE_DIRECTION_B);
ut_ad(dir >= PAGE_LEFT);
ut_ad(dir <= PAGE_NO_DIRECTION);
*ptr = (*ptr & ~((1U << 3) - 1)) | dir;
}
/** Read the PAGE_INSTANT field.
@param[in] page index page
@return the value of the PAGE_INSTANT field */
......
......@@ -243,17 +243,17 @@ page_zip_write_header(
mtr_t* mtr) /*!< in: mini-transaction, or NULL */
MY_ATTRIBUTE((nonnull(1,2)));
/**********************************************************************//**
Write an entire record on the compressed page. The data must already
have been written to the uncompressed page. */
void
page_zip_write_rec(
/*===============*/
page_zip_des_t* page_zip,/*!< in/out: compressed page */
const byte* rec, /*!< in: record being written */
dict_index_t* index, /*!< in: the index the record belongs to */
const offset_t* offsets,/*!< in: rec_get_offsets(rec, index) */
ulint create) /*!< in: nonzero=insert, zero=update */
/** Write an entire record to the ROW_FORMAT=COMPRESSED page.
The data must already have been written to the uncompressed page.
@param[in,out] page_zip ROW_FORMAT=COMPRESSED page
@param[in] rec record in the uncompressed page
@param[in] index the index that the page belongs to
@param[in] offsets rec_get_offsets(rec, index)
@param[in] create nonzero=insert, zero=update
@param[in,out] mtr mini-transaction */
void page_zip_write_rec(page_zip_des_t *page_zip, const byte *rec,
const dict_index_t *index, const offset_t *offsets,
ulint create, mtr_t *mtr)
MY_ATTRIBUTE((nonnull));
/***********************************************************//**
......@@ -374,7 +374,9 @@ page_zip_dir_insert(
page_cur_t* cursor, /*!< in/out: page cursor */
const byte* free_rec,/*!< in: record from which rec was
allocated, or NULL */
byte* rec); /*!< in: record to insert */
byte* rec, /*!< in: record to insert */
mtr_t* mtr) /*!< in/out: mini-transaction */
MY_ATTRIBUTE((nonnull(1,3,4)));
/**********************************************************************//**
Shift the dense page directory and the array of BLOB pointers
......@@ -391,16 +393,6 @@ page_zip_dir_delete(
mtr_t* mtr) /*!< in/out: mini-transaction */
MY_ATTRIBUTE((nonnull(1,2,3,4,6)));
/**********************************************************************//**
Add a slot to the dense page directory. */
void
page_zip_dir_add_slot(
/*==================*/
page_zip_des_t* page_zip, /*!< in/out: compressed page */
ulint is_clustered) /*!< in: nonzero for clustered index,
zero for others */
MY_ATTRIBUTE((nonnull));
/***********************************************************//**
Parses a log record of writing to the header of a page.
@return end of log record or NULL */
......
......@@ -250,6 +250,7 @@ rec_get_n_owned_new(
/*================*/
const rec_t* rec) /*!< in: new-style physical record */
MY_ATTRIBUTE((warn_unused_result));
/******************************************************//**
The following function is used to retrieve the info bits of
a record.
......@@ -261,24 +262,6 @@ rec_get_info_bits(
const rec_t* rec, /*!< in: physical record */
ulint comp) /*!< in: nonzero=compact page format */
MY_ATTRIBUTE((warn_unused_result));
/******************************************************//**
The following function is used to set the info bits of a record. */
UNIV_INLINE
void
rec_set_info_bits_old(
/*==================*/
rec_t* rec, /*!< in: old-style physical record */
ulint bits) /*!< in: info bits */
MY_ATTRIBUTE((nonnull));
/******************************************************//**
The following function is used to set the info bits of a record. */
UNIV_INLINE
void
rec_set_info_bits_new(
/*==================*/
rec_t* rec, /*!< in/out: new-style physical record */
ulint bits) /*!< in: info bits */
MY_ATTRIBUTE((nonnull));
/** Determine the status bits of a non-REDUNDANT record.
@param[in] rec ROW_FORMAT=COMPACT,DYNAMIC,COMPRESSED record
......@@ -859,26 +842,6 @@ rec_offs_n_extern(
/*==============*/
const offset_t* offsets)/*!< in: array returned by rec_get_offsets() */
MY_ATTRIBUTE((warn_unused_result));
/***********************************************************//**
This is used to modify the value of an already existing field in a record.
The previous value must have exactly the same size as the new value. If len
is UNIV_SQL_NULL then the field is treated as an SQL null.
For records in ROW_FORMAT=COMPACT (new-style records), len must not be
UNIV_SQL_NULL unless the field already is SQL null. */
UNIV_INLINE
void
rec_set_nth_field(
/*==============*/
rec_t* rec, /*!< in: record */
const offset_t* offsets,/*!< in: array returned by rec_get_offsets() */
ulint n, /*!< in: index number of the field */
const void* data, /*!< in: pointer to the data if not SQL null */
ulint len) /*!< in: length of the data or UNIV_SQL_NULL.
If not SQL null, must have the same
length as the previous value.
If SQL null, previous value must be
SQL null. */
MY_ATTRIBUTE((nonnull(1,2)));
/**********************************************************//**
The following function returns the data size of an old-style physical
record, that is the sum of field lengths. SQL null fields
......
......@@ -123,23 +123,6 @@ and the shift needed to obtain each bit-field of the record. */
# error "sum of new-style masks != 0xFFFFFFUL"
#endif
/***********************************************************//**
Sets the value of the ith field SQL null bit of an old-style record. */
void
rec_set_nth_field_null_bit(
/*=======================*/
rec_t* rec, /*!< in: record */
ulint i, /*!< in: ith field */
ibool val); /*!< in: value to set */
/***********************************************************//**
Sets an old-style record field to SQL null.
The physical size of the field is not changed. */
void
rec_set_nth_field_sql_null(
/*=======================*/
rec_t* rec, /*!< in: record */
ulint n); /*!< in: index of the field */
/******************************************************//**
Gets a bit field from within 1 byte. */
UNIV_INLINE
......@@ -533,31 +516,6 @@ rec_get_info_bits(
return(val);
}
/******************************************************//**
The following function is used to set the info bits of a record. */
UNIV_INLINE
void
rec_set_info_bits_old(
/*==================*/
rec_t* rec, /*!< in: old-style physical record */
ulint bits) /*!< in: info bits */
{
rec_set_bit_field_1(rec, bits, REC_OLD_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
}
/******************************************************//**
The following function is used to set the info bits of a record. */
UNIV_INLINE
void
rec_set_info_bits_new(
/*==================*/
rec_t* rec, /*!< in/out: new-style physical record */
ulint bits) /*!< in: info bits */
{
rec_set_bit_field_1(rec, bits, REC_NEW_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
}
/******************************************************//**
The following function is used to retrieve the info and status
bits of a record. (Only compact records have status bits.)
......@@ -594,7 +552,9 @@ rec_set_info_and_status_bits(
compile_time_assert(!((REC_NEW_STATUS_MASK >> REC_NEW_STATUS_SHIFT)
& (REC_INFO_BITS_MASK >> REC_INFO_BITS_SHIFT)));
rec_set_status(rec, bits & REC_NEW_STATUS_MASK);
rec_set_info_bits_new(rec, bits & ~REC_NEW_STATUS_MASK);
rec_set_bit_field_1(rec, bits & ~REC_NEW_STATUS_MASK,
REC_NEW_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
}
/******************************************************//**
......@@ -1032,50 +992,6 @@ rec_get_nth_field_size(
return(next_os - os);
}
/***********************************************************//**
This is used to modify the value of an already existing field in a record.
The previous value must have exactly the same size as the new value. If len
is UNIV_SQL_NULL then the field is treated as an SQL null.
For records in ROW_FORMAT=COMPACT (new-style records), len must not be
UNIV_SQL_NULL unless the field already is SQL null. */
UNIV_INLINE
void
rec_set_nth_field(
/*==============*/
rec_t* rec, /*!< in: record */
const offset_t* offsets,/*!< in: array returned by rec_get_offsets() */
ulint n, /*!< in: index number of the field */
const void* data, /*!< in: pointer to the data
if not SQL null */
ulint len) /*!< in: length of the data or UNIV_SQL_NULL */
{
byte* data2;
ulint len2;
ut_ad(rec_offs_validate(rec, NULL, offsets));
ut_ad(!rec_offs_nth_default(offsets, n));
if (len == UNIV_SQL_NULL) {
if (!rec_offs_nth_sql_null(offsets, n)) {
ut_a(!rec_offs_comp(offsets));
rec_set_nth_field_sql_null(rec, n);
}
return;
}
data2 = (byte*)rec_get_nth_field(rec, offsets, n, &len2);
if (len2 == UNIV_SQL_NULL) {
ut_ad(!rec_offs_comp(offsets));
rec_set_nth_field_null_bit(rec, n, FALSE);
ut_ad(len == rec_get_nth_field_size(rec, n));
} else {
ut_ad(len2 == len);
}
memcpy(data2, data, len);
}
/**********************************************************//**
The following function returns the data size of an old-style physical
record, that is the sum of field lengths. SQL null fields
......
......@@ -108,17 +108,6 @@ upd_node_create(
/*============*/
mem_heap_t* heap); /*!< in: mem heap where created */
/***********************************************************//**
Writes to the redo log the new values of the fields occurring in the index. */
void
row_upd_index_write_log(
/*====================*/
const upd_t* update, /*!< in: update vector */
byte* log_ptr,/*!< in: pointer to mlog buffer: must
contain at least MLOG_BUF_MARGIN bytes
of free space; the buffer is closed
within this function */
mtr_t* mtr); /*!< in: mtr into whose log to write */
/***********************************************************//**
Returns TRUE if row update changes size of some field in index or if some
field to be updated is stored externally in rec or update.
@return TRUE if the update changes the size of some field in index or
......@@ -137,21 +126,6 @@ row_upd_changes_disowned_external(
/*==============================*/
const upd_t* update) /*!< in: update vector */
MY_ATTRIBUTE((nonnull, warn_unused_result));
/***********************************************************//**
Replaces the new column values stored in the update vector to the
record given. No field size changes are allowed. This function is
usually invoked on a clustered index. The only use case for a
secondary index is row_ins_sec_index_entry_by_modify() or its
counterpart in ibuf_insert_to_index_page(). */
void
row_upd_rec_in_place(
/*=================*/
rec_t* rec, /*!< in/out: record where replaced */
dict_index_t* index, /*!< in: the index the record belongs to */
const offset_t* offsets,/*!< in: array returned by rec_get_offsets() */
const upd_t* update, /*!< in: update vector */
page_zip_des_t* page_zip);/*!< in: compressed page with enough space
available, or NULL */
/***************************************************************//**
Builds an update vector from those fields which in a secondary index entry
......@@ -345,6 +319,7 @@ row_upd_step(
/*********************************************************************//**
Parses the log data written by row_upd_index_write_log.
@return log data end or NULL */
ATTRIBUTE_COLD /* only used when crash-upgrading */
byte*
row_upd_index_parse(
/*================*/
......
......@@ -323,15 +323,20 @@ class mlog_init_t
i.first, 0, RW_X_LATCH, NULL,
BUF_GET_IF_IN_POOL, __FILE__, __LINE__,
&mtr)) {
if (UNIV_LIKELY_NULL(block->page.zip.data)
&& fil_page_type_is_index(
fil_page_get_type(
block->page.zip.data))
&& !page_zip_decompress(&block->page.zip,
if (UNIV_LIKELY_NULL(block->page.zip.data)) {
switch (fil_page_get_type(
block->page.zip.data)) {
case FIL_PAGE_INDEX:
case FIL_PAGE_RTREE:
if (page_zip_decompress(
&block->page.zip,
block->frame,
true)) {
ib::error() << "corrupted page "
<< block->page.id;
break;
}
ib::error() << "corrupted "
<< block->page.id;
}
}
if (recv_no_ibuf_operations) {
mtr.commit();
......@@ -1465,6 +1470,10 @@ recv_parse_or_apply_log_rec_body(
switch (type) {
case MLOG_1BYTE: case MLOG_2BYTES: case MLOG_4BYTES: case MLOG_8BYTES:
ut_ad(!page_zip
|| fil_page_get_type(page_zip->data)
<= FIL_PAGE_TYPE_ZBLOB2);
/* fall through */
case MLOG_MEMSET:
#ifdef UNIV_DEBUG
if (page && page_type == FIL_PAGE_TYPE_ALLOCATED
......@@ -1641,7 +1650,8 @@ recv_parse_or_apply_log_rec_body(
|| (ibool)!!page_is_comp(page)
== dict_table_is_comp(index->table));
ptr = btr_cur_parse_update_in_place(ptr, end_ptr, page,
page_zip, index);
page_zip, index,
mtr);
}
break;
case MLOG_LIST_END_DELETE: case MLOG_COMP_LIST_END_DELETE:
......
This diff is collapsed.
This diff is collapsed.
......@@ -1322,61 +1322,6 @@ rec_get_converted_size_comp(
return(ULINT_UNDEFINED);
}
/***********************************************************//**
Sets the value of the ith field SQL null bit of an old-style record. */
void
rec_set_nth_field_null_bit(
/*=======================*/
rec_t* rec, /*!< in: record */
ulint i, /*!< in: ith field */
ibool val) /*!< in: value to set */
{
ulint info;
if (rec_get_1byte_offs_flag(rec)) {
info = rec_1_get_field_end_info(rec, i);
if (val) {
info = info | REC_1BYTE_SQL_NULL_MASK;
} else {
info = info & ~REC_1BYTE_SQL_NULL_MASK;
}
rec_1_set_field_end_info(rec, i, info);
return;
}
info = rec_2_get_field_end_info(rec, i);
if (val) {
info = info | REC_2BYTE_SQL_NULL_MASK;
} else {
info = info & ~REC_2BYTE_SQL_NULL_MASK;
}
rec_2_set_field_end_info(rec, i, info);
}
/***********************************************************//**
Sets an old-style record field to SQL null.
The physical size of the field is not changed. */
void
rec_set_nth_field_sql_null(
/*=======================*/
rec_t* rec, /*!< in: record */
ulint n) /*!< in: index of the field */
{
ulint offset;
offset = rec_get_field_start_offs(rec, n);
data_write_sql_null(rec + offset, rec_get_nth_field_size(rec, n));
rec_set_nth_field_null_bit(rec, n, TRUE);
}
/*********************************************************//**
Builds an old-style physical record out of a data tuple and
stores it beginning from the start of the given buffer.
......@@ -1414,8 +1359,10 @@ rec_convert_dtuple_to_rec_old(
rec_set_n_fields_old(rec, n_fields);
/* Set the info bits of the record */
rec_set_info_bits_old(rec, dtuple_get_info_bits(dtuple)
& REC_INFO_BITS_MASK);
rec_set_bit_field_1(rec,
dtuple_get_info_bits(dtuple) & REC_INFO_BITS_MASK,
REC_OLD_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
rec_set_bit_field_2(rec, PAGE_HEAP_NO_USER_LOW, REC_OLD_HEAP_NO,
REC_HEAP_NO_MASK, REC_HEAP_NO_SHIFT);
......@@ -1738,7 +1685,9 @@ rec_convert_dtuple_to_rec_new(
status, false);
}
rec_set_info_bits_new(buf, dtuple->info_bits & ~REC_NEW_STATUS_MASK);
rec_set_bit_field_1(buf, dtuple->info_bits & ~REC_NEW_STATUS_MASK,
REC_NEW_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
return buf;
}
......
......@@ -585,163 +585,10 @@ row_upd_changes_disowned_external(
return(false);
}
/***********************************************************//**
Replaces the new column values stored in the update vector to the
record given. No field size changes are allowed. This function is
usually invoked on a clustered index. The only use case for a
secondary index is row_ins_sec_index_entry_by_modify() or its
counterpart in ibuf_insert_to_index_page(). */
void
row_upd_rec_in_place(
/*=================*/
rec_t* rec, /*!< in/out: record where replaced */
dict_index_t* index, /*!< in: the index the record belongs to */
const offset_t* offsets,/*!< in: array returned by rec_get_offsets() */
const upd_t* update, /*!< in: update vector */
page_zip_des_t* page_zip)/*!< in: compressed page with enough space
available, or NULL */
{
const upd_field_t* upd_field;
const dfield_t* new_val;
ulint n_fields;
ulint i;
ut_ad(rec_offs_validate(rec, index, offsets));
ut_ad(!index->table->skip_alter_undo);
if (rec_offs_comp(offsets)) {
#ifdef UNIV_DEBUG
switch (rec_get_status(rec)) {
case REC_STATUS_ORDINARY:
break;
case REC_STATUS_INSTANT:
ut_ad(index->is_instant());
break;
case REC_STATUS_NODE_PTR:
if (index->is_dummy
&& fil_page_get_type(page_align(rec))
== FIL_PAGE_RTREE) {
/* The function rtr_update_mbr_field_in_place()
is generating MLOG_COMP_REC_UPDATE_IN_PLACE
and MLOG_REC_UPDATE_IN_PLACE records for
node pointer pages. */
break;
}
/* fall through */
case REC_STATUS_INFIMUM:
case REC_STATUS_SUPREMUM:
ut_ad(!"wrong record status in update");
}
#endif /* UNIV_DEBUG */
rec_set_info_bits_new(rec, update->info_bits);
} else {
rec_set_info_bits_old(rec, update->info_bits);
}
n_fields = upd_get_n_fields(update);
for (i = 0; i < n_fields; i++) {
upd_field = upd_get_nth_field(update, i);
/* No need to update virtual columns for non-virtual index */
if (upd_fld_is_virtual_col(upd_field)
&& !dict_index_has_virtual(index)) {
continue;
}
new_val = &(upd_field->new_val);
ut_ad(!dfield_is_ext(new_val) ==
!rec_offs_nth_extern(offsets, upd_field->field_no));
rec_set_nth_field(rec, offsets, upd_field->field_no,
dfield_get_data(new_val),
dfield_get_len(new_val));
}
if (page_zip) {
page_zip_write_rec(page_zip, rec, index, offsets, 0);
}
}
/***********************************************************//**
Writes to the redo log the new values of the fields occurring in the index. */
void
row_upd_index_write_log(
/*====================*/
const upd_t* update, /*!< in: update vector */
byte* log_ptr,/*!< in: pointer to mlog buffer: must
contain at least MLOG_BUF_MARGIN bytes
of free space; the buffer is closed
within this function */
mtr_t* mtr) /*!< in: mtr into whose log to write */
{
const upd_field_t* upd_field;
const dfield_t* new_val;
ulint len;
ulint n_fields;
byte* buf_end;
ulint i;
n_fields = upd_get_n_fields(update);
buf_end = log_ptr + MLOG_BUF_MARGIN;
mach_write_to_1(log_ptr, update->info_bits);
log_ptr++;
log_ptr += mach_write_compressed(log_ptr, n_fields);
for (i = 0; i < n_fields; i++) {
compile_time_assert(MLOG_BUF_MARGIN > 30);
if (log_ptr + 30 > buf_end) {
mlog_close(mtr, log_ptr);
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
buf_end = log_ptr + MLOG_BUF_MARGIN;
}
upd_field = upd_get_nth_field(update, i);
new_val = &(upd_field->new_val);
len = dfield_get_len(new_val);
/* If this is a virtual column, mark it using special
field_no */
ulint field_no = upd_fld_is_virtual_col(upd_field)
? REC_MAX_N_FIELDS + unsigned(upd_field->field_no)
: unsigned(upd_field->field_no);
log_ptr += mach_write_compressed(log_ptr, field_no);
log_ptr += mach_write_compressed(log_ptr, len);
if (len != UNIV_SQL_NULL) {
if (log_ptr + len < buf_end) {
memcpy(log_ptr, dfield_get_data(new_val), len);
log_ptr += len;
} else {
mlog_close(mtr, log_ptr);
mlog_catenate_string(
mtr,
static_cast<const byte*>(
dfield_get_data(new_val)),
len);
log_ptr = mlog_open(mtr, MLOG_BUF_MARGIN);
buf_end = log_ptr + MLOG_BUF_MARGIN;
}
}
}
mlog_close(mtr, log_ptr);
}
/*********************************************************************//**
Parses the log data written by row_upd_index_write_log.
@return log data end or NULL */
ATTRIBUTE_COLD /* only used when crash-upgrading */
byte*
row_upd_index_parse(
/*================*/
......
......@@ -2438,7 +2438,48 @@ trx_undo_prev_version_build(
*old_vers = rec_copy(buf, rec, offsets);
rec_offs_make_valid(*old_vers, index, true, offsets);
row_upd_rec_in_place(*old_vers, index, offsets, update, NULL);
rec_set_bit_field_1(*old_vers, update->info_bits,
rec_offs_comp(offsets)
? REC_NEW_INFO_BITS : REC_OLD_INFO_BITS,
REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT);
for (ulint i = 0; i < update->n_fields; i++) {
const upd_field_t* uf = upd_get_nth_field(update, i);
if (upd_fld_is_virtual_col(uf)) {
/* There are no virtual columns in
a clustered index record. */
continue;
}
const ulint n = uf->field_no;
ut_ad(!dfield_is_ext(&uf->new_val)
== !rec_offs_nth_extern(offsets, n));
ut_ad(!rec_offs_nth_default(offsets, n));
if (UNIV_UNLIKELY(dfield_is_null(&uf->new_val))) {
ut_ad(!rec_offs_nth_sql_null(offsets, n));
ut_ad(!index->table->not_redundant());
ulint l = rec_get_1byte_offs_flag(*old_vers)
? (n + 1) : (n + 1) * 2;
(*old_vers)[-REC_N_OLD_EXTRA_BYTES - l]
|= REC_1BYTE_SQL_NULL_MASK;
compile_time_assert(REC_1BYTE_SQL_NULL_MASK << 8
== REC_2BYTE_SQL_NULL_MASK);
continue;
}
ulint len;
memcpy(rec_get_nth_field(*old_vers, offsets, n, &len),
uf->new_val.data, uf->new_val.len);
if (UNIV_UNLIKELY(len != uf->new_val.len)) {
ut_ad(len == UNIV_SQL_NULL);
ut_ad(!rec_offs_comp(offsets));
ut_ad(uf->new_val.len
== rec_get_nth_field_size(rec, n));
ulint l = rec_get_1byte_offs_flag(*old_vers)
? (n + 1) : (n + 1) * 2;
(*old_vers)[-REC_N_OLD_EXTRA_BYTES - l]
&= ~REC_1BYTE_SQL_NULL_MASK;
}
}
}
/* Set the old value (which is the after image of an update) in the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment