InnoDB Plugin
1.0
|
#include "univ.i"
#include "dict0dict.h"
#include "data0data.h"
#include "page0cur.h"
#include "mtr0mtr.h"
#include "btr0types.h"
#include "btr0btr.ic"
Go to the source code of this file.
Macros | |
#define | BTR_PAGE_MAX_REC_SIZE (UNIV_PAGE_SIZE / 2 - 200) |
#define | BTR_MAX_LEVELS 100 |
Maximum depth of a B-tree in InnoDB. | |
#define | BTR_INSERT 512 |
#define | BTR_ESTIMATE 1024 |
#define | BTR_IGNORE_SEC_UNIQUE 2048 |
#define | BTR_DELETE_MARK 4096 |
#define | BTR_DELETE 8192 |
#define | BTR_ALREADY_S_LATCHED 16384 |
#define | BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode) |
#define | btr_assert_not_corrupted(block, index) |
#define | btr_blob_dbg_add_blob(rec, field_no, page, index, ctx) ((void) 0) |
#define | btr_blob_dbg_set_deleted_flag(rec, index, offsets, del) ((void) 0) |
#define | btr_blob_dbg_owner(rec, index, offsets, i, val) ((void) 0) |
#define | btr_blob_dbg_assert_empty(index, page_no) ((void) 0) |
#define | btr_block_get(space, zip_size, page_no, mode, index, mtr) |
#define | btr_page_get(space, zip_size, page_no, mode, idx, mtr) buf_block_get_frame(btr_block_get(space,zip_size,page_no,mode,idx,mtr)) |
#define | btr_page_get_level(page, mtr) btr_page_get_level_low(page) |
#define | btr_insert_on_non_leaf_level(f, i, l, t, m) btr_insert_on_non_leaf_level_func(f,i,l,t,__FILE__,__LINE__,m) |
#define | BTR_N_LEAF_PAGES 1 |
#define | BTR_TOTAL_SIZE 2 |
Enumerations | |
enum | btr_latch_mode { BTR_SEARCH_LEAF = RW_S_LATCH, BTR_MODIFY_LEAF = RW_X_LATCH, BTR_NO_LATCHES = RW_NO_LATCH, BTR_MODIFY_TREE = 33, BTR_CONT_MODIFY_TREE = 34, BTR_SEARCH_PREV = 35, BTR_MODIFY_PREV = 36 } |
Functions | |
UNIV_INTERN void | btr_corruption_report (const buf_block_t *block, const dict_index_t *index) UNIV_COLD) |
UNIV_INTERN page_t * | btr_root_get (const dict_index_t *index, mtr_t *mtr)) |
UNIV_INTERN dberr_t | btr_root_adjust_on_import (const dict_index_t *index)) |
UNIV_INTERN ulint | btr_height_get (dict_index_t *index, mtr_t *mtr)) |
UNIV_INLINE buf_block_t * | btr_block_get_func (ulint space, ulint zip_size, ulint page_no, ulint mode, const char *file, ulint line, const dict_index_t *index, mtr_t *mtr) |
UNIV_INLINE index_id_t | btr_page_get_index_id (const page_t *page)) |
UNIV_INLINE ulint | btr_page_get_level_low (const page_t *page)) |
UNIV_INLINE ulint | btr_page_get_next (const page_t *page, mtr_t *mtr)) |
UNIV_INLINE ulint | btr_page_get_prev (const page_t *page, mtr_t *mtr)) |
UNIV_INTERN rec_t * | btr_get_prev_user_rec (rec_t *rec, mtr_t *mtr)) |
UNIV_INTERN rec_t * | btr_get_next_user_rec (rec_t *rec, mtr_t *mtr)) |
UNIV_INLINE void | btr_leaf_page_release (buf_block_t *block, ulint latch_mode, mtr_t *mtr)) |
UNIV_INLINE ulint | btr_node_ptr_get_child_page_no (const rec_t *rec, const ulint *offsets)) |
UNIV_INTERN ulint | btr_create (ulint type, ulint space, ulint zip_size, index_id_t index_id, dict_index_t *index, mtr_t *mtr)) |
UNIV_INTERN void | btr_free_but_not_root (ulint space, ulint zip_size, ulint root_page_no) |
UNIV_INTERN void | btr_free_root (ulint space, ulint zip_size, ulint root_page_no, mtr_t *mtr)) |
UNIV_INTERN rec_t * | btr_root_raise_and_insert (ulint flags, btr_cur_t *cursor, ulint **offsets, mem_heap_t **heap, const dtuple_t *tuple, ulint n_ext, mtr_t *mtr)) |
UNIV_INTERN bool | btr_page_reorganize_low (bool recovery, ulint z_level, page_cur_t *cursor, dict_index_t *index, mtr_t *mtr)) |
UNIV_INTERN bool | btr_page_reorganize (page_cur_t *cursor, dict_index_t *index, mtr_t *mtr)) |
UNIV_INTERN ibool | btr_page_get_split_rec_to_left (btr_cur_t *cursor, rec_t **split_rec)) |
UNIV_INTERN ibool | btr_page_get_split_rec_to_right (btr_cur_t *cursor, rec_t **split_rec)) |
UNIV_INTERN rec_t * | btr_page_split_and_insert (ulint flags, btr_cur_t *cursor, ulint **offsets, mem_heap_t **heap, const dtuple_t *tuple, ulint n_ext, mtr_t *mtr)) |
UNIV_INTERN void | btr_insert_on_non_leaf_level_func (ulint flags, dict_index_t *index, ulint level, dtuple_t *tuple, const char *file, ulint line, mtr_t *mtr)) |
UNIV_INTERN void | btr_set_min_rec_mark (rec_t *rec, mtr_t *mtr)) |
UNIV_INTERN void | btr_node_ptr_delete (dict_index_t *index, buf_block_t *block, mtr_t *mtr)) |
UNIV_INTERN ibool | btr_check_node_ptr (dict_index_t *index, buf_block_t *block, mtr_t *mtr)) |
UNIV_INTERN ibool | btr_compress (btr_cur_t *cursor, ibool adjust, mtr_t *mtr)) |
UNIV_INTERN void | btr_discard_page (btr_cur_t *cursor, mtr_t *mtr)) |
UNIV_INTERN byte * | btr_parse_set_min_rec_mark (byte *ptr, byte *end_ptr, ulint comp, page_t *page, mtr_t *mtr) |
UNIV_INTERN byte * | btr_parse_page_reorganize (byte *ptr, byte *end_ptr, dict_index_t *index, bool compressed, buf_block_t *block, mtr_t *mtr) |
UNIV_INTERN ulint | btr_get_size (dict_index_t *index, ulint flag, mtr_t *mtr)) |
UNIV_INTERN buf_block_t * | btr_page_alloc (dict_index_t *index, ulint hint_page_no, byte file_direction, ulint level, mtr_t *mtr, mtr_t *init_mtr)) |
UNIV_INTERN void | btr_page_free (dict_index_t *index, buf_block_t *block, mtr_t *mtr)) |
UNIV_INTERN void | btr_page_free_low (dict_index_t *index, buf_block_t *block, ulint level, mtr_t *mtr)) |
UNIV_INTERN ibool | btr_index_rec_validate (const rec_t *rec, const dict_index_t *index, ibool dump_on_error)) |
UNIV_INTERN bool | btr_validate_index (dict_index_t *index, const trx_t *trx) |
Variables | |
UNIV_INTERN byte | warn_unused_result |
The B-tree
Created 6/2/1994 Heikki Tuuri
#define BTR_ALREADY_S_LATCHED 16384 |
In the case of BTR_SEARCH_LEAF or BTR_MODIFY_LEAF, the caller is
already holding an S latch on the index tree
#define btr_assert_not_corrupted | ( | block, | |
index | |||
) |
Assert that a B-tree page is not corrupted.
block | buffer block containing a B-tree page |
index | the B-tree index |
#define btr_block_get | ( | space, | |
zip_size, | |||
page_no, | |||
mode, | |||
index, | |||
mtr | |||
) |
Gets a buffer page and declares its latching order level.
space | tablespace identifier |
zip_size | compressed page size in bytes or 0 for uncompressed pages |
page_no | page number |
mode | latch mode |
index | index tree, may be NULL if not the insert buffer tree |
mtr | mini-transaction handle |
#define BTR_DELETE 8192 |
Try to purge the record at the searched position using the insert/delete
buffer when the record is not in the buffer pool.
#define BTR_DELETE_MARK 4096 |
Try to delete mark the record at the searched position using the
insert/delete buffer when the record is not in the buffer pool.
#define BTR_ESTIMATE 1024 |
This flag ORed to btr_latch_mode says that we do the search in query
optimization
#define BTR_IGNORE_SEC_UNIQUE 2048 |
This flag ORed to BTR_INSERT says that we can ignore possible
UNIQUE definition on secondary indexes when we decide if we can use the insert buffer to speed up inserts
#define BTR_INSERT 512 |
If this is ORed to btr_latch_mode, it means that the search tuple
will be inserted to the index, at the searched position. When the record is not in the buffer pool, try to use the insert buffer.
#define BTR_LATCH_MODE_WITHOUT_FLAGS | ( | latch_mode | ) |
#define BTR_MAX_LEVELS 100 |
Maximum depth of a B-tree in InnoDB.
Note that this isn't a maximum as such; none of the tree operations avoid producing trees bigger than this. It is instead a "max depth that other code must work with", useful for e.g. fixed-size arrays that must store some information about each level in a tree. In other words: if a B-tree with bigger depth than this is encountered, it is not acceptable for it to lead to mysterious memory corruption, but it is acceptable for the program to die with a clear assert failure.
#define btr_page_get | ( | space, | |
zip_size, | |||
page_no, | |||
mode, | |||
idx, | |||
mtr | |||
) | buf_block_get_frame(btr_block_get(space,zip_size,page_no,mode,idx,mtr)) |
Gets a buffer page and declares its latching order level.
space | tablespace identifier |
zip_size | compressed page size in bytes or 0 for uncompressed pages |
page_no | page number |
mode | latch mode |
idx | index tree, may be NULL if not the insert buffer tree |
mtr | mini-transaction handle |
#define BTR_PAGE_MAX_REC_SIZE (UNIV_PAGE_SIZE / 2 - 200) |
Maximum record size which can be stored on a page, without using the
special big record storage structure
enum btr_latch_mode |
Latching modes for btr_cur_search_to_nth_level().
UNIV_INLINE buf_block_t* btr_block_get_func | ( | ulint | space, |
ulint | zip_size, | ||
ulint | page_no, | ||
ulint | mode, | ||
const char * | file, | ||
ulint | line, | ||
const dict_index_t * | index, | ||
mtr_t * | mtr | ||
) |
Gets a buffer page and declares its latching order level. in/out: mini-transaction
Gets a buffer page and declares its latching order level.
space | in: space id |
zip_size | in: compressed page size in bytes or 0 for uncompressed pages |
page_no | in: page number |
mode | in: latch mode |
file | in: file name |
line | in: line where called |
index | in: index tree, may be NULL if it is not an insert buffer tree |
mtr | in/out: mtr |
UNIV_INTERN ibool btr_check_node_ptr | ( | dict_index_t * | index, |
buf_block_t * | block, | ||
mtr_t * | mtr | ||
) |
Checks that the node pointer to a page is appropriate.
index | in: index tree |
block | in: index page |
mtr | in: mtr |
Tries to merge the page first to the left immediate brother if such a brother exists, and the node pointers to the current page and to the brother reside on the same page. If the left brother does not satisfy these conditions, looks at the right brother. If the page is the only one on that level lifts the records of the page to the father page, thus reducing the tree height. It is assumed that mtr holds an x-latch on the tree and on the page. If cursor is on the leaf level, mtr must also hold x-latches to the brothers, if they exist.
cursor | in/out: cursor on the page to merge or lift; the page must not be empty: when deleting records, use btr_discard_page() if the page would become empty |
adjust | in: TRUE if should adjust the cursor position even if compression occurs |
mtr | in/out: mini-transaction |
UNIV_INTERN void btr_corruption_report | ( | const buf_block_t * | block, |
const dict_index_t * | index | ||
) |
Report that an index page is corrupted.
block | in: corrupted block |
index | in: index tree |
UNIV_INTERN ulint btr_create | ( | ulint | type, |
ulint | space, | ||
ulint | zip_size, | ||
index_id_t | index_id, | ||
dict_index_t * | index, | ||
mtr_t * | mtr | ||
) |
Creates the root node for a new index tree.
type | in: type of the index |
space | in: space where created |
zip_size | in: compressed page size in bytes or 0 for uncompressed pages |
index_id | in: index id |
index | in: index |
mtr | in: mini-transaction handle |
Discards a page from a B-tree. This is used to remove the last record from a B-tree page: the whole page must be removed at the same time. This cannot be used for the root page, which is allowed to be empty.
cursor | in: cursor on the page to discard: not on the root page |
mtr | in: mtr |
UNIV_INTERN void btr_free_but_not_root | ( | ulint | space, |
ulint | zip_size, | ||
ulint | root_page_no | ||
) |
Frees a B-tree except the root page, which MUST be freed after this by calling btr_free_root. in: root page number
space | in: space where created |
zip_size | in: compressed page size in bytes or 0 for uncompressed pages |
UNIV_INTERN void btr_free_root | ( | ulint | space, |
ulint | zip_size, | ||
ulint | root_page_no, | ||
mtr_t * | mtr | ||
) |
Frees the B-tree root page. Other tree MUST already have been freed.
space | in: space where created |
zip_size | in: compressed page size in bytes or 0 for uncompressed pages |
root_page_no | in: root page number |
mtr | in/out: mini-transaction |
UNIV_INTERN rec_t* btr_get_next_user_rec | ( | rec_t * | rec, |
mtr_t * | mtr | ||
) |
Gets pointer to the next user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor.
rec | in: record on leaf level |
mtr | in: mtr holding a latch on the page, and if needed, also to the next page |
UNIV_INTERN rec_t* btr_get_prev_user_rec | ( | rec_t * | rec, |
mtr_t * | mtr | ||
) |
Gets pointer to the previous user record in the tree. It is assumed that the caller has appropriate latches on the page and its neighbor.
rec | in: record on leaf level |
mtr | in: mtr holding a latch on the page, and if needed, also to the previous page |
UNIV_INTERN ulint btr_get_size | ( | dict_index_t * | index, |
ulint | flag, | ||
mtr_t * | mtr | ||
) |
Gets the number of pages in a B-tree.
index | in: index |
flag | in: BTR_N_LEAF_PAGES or BTR_TOTAL_SIZE |
mtr | in/out: mini-transaction where index is s-latched |
UNIV_INTERN ulint btr_height_get | ( | dict_index_t * | index, |
mtr_t * | mtr | ||
) |
Gets the height of the B-tree (the level of the root, when the leaf level is assumed to be 0). The caller must hold an S or X latch on the index.
index | in: index tree |
mtr | in/out: mini-transaction |
UNIV_INTERN ibool btr_index_rec_validate | ( | const rec_t * | rec, |
const dict_index_t * | index, | ||
ibool | dump_on_error | ||
) |
Checks the size and number of fields in a record based on the definition of the index.
rec | in: index record |
index | in: index |
dump_on_error | in: TRUE if the function should print hex dump of record and page on error |
UNIV_INTERN void btr_insert_on_non_leaf_level_func | ( | ulint | flags, |
dict_index_t * | index, | ||
ulint | level, | ||
dtuple_t * | tuple, | ||
const char * | file, | ||
ulint | line, | ||
mtr_t * | mtr | ||
) |
Inserts a data tuple to a tree on a non-leaf level. It is assumed that mtr holds an x-latch on the tree.
flags | in: undo logging and locking flags |
index | in: index |
level | in: level, must be > 0 |
tuple | in: the record to be inserted |
file | in: file name |
line | in: line where called |
mtr | in: mtr |
UNIV_INLINE void btr_leaf_page_release | ( | buf_block_t * | block, |
ulint | latch_mode, | ||
mtr_t * | mtr | ||
) |
Releases the latch on a leaf page and bufferunfixes it.
Releases the latches on a leaf page and bufferunfixes it.
block | in: buffer block |
latch_mode | in: BTR_SEARCH_LEAF or BTR_MODIFY_LEAF |
mtr | in: mtr |
UNIV_INTERN void btr_node_ptr_delete | ( | dict_index_t * | index, |
buf_block_t * | block, | ||
mtr_t * | mtr | ||
) |
Deletes on the upper level the node pointer to a page.
index | in: index tree |
block | in: page whose node pointer is deleted |
mtr | in: mtr |
UNIV_INLINE ulint btr_node_ptr_get_child_page_no | ( | const rec_t * | rec, |
const ulint * | offsets | ||
) |
Gets the child node file address in a node pointer. NOTE: the offsets array must contain all offsets for the record since we read the last field according to offsets and assume that it contains the child page number. In other words offsets must have been retrieved with rec_get_offsets(n_fields=ULINT_UNDEFINED).
rec | in: node pointer record |
offsets | in: array returned by rec_get_offsets() |
UNIV_INTERN buf_block_t* btr_page_alloc | ( | dict_index_t * | index, |
ulint | hint_page_no, | ||
byte | file_direction, | ||
ulint | level, | ||
mtr_t * | mtr, | ||
mtr_t * | init_mtr | ||
) |
Allocates a new file page to be used in an index tree. NOTE: we assume that the caller has made the reservation for free extents!
NULL | if no page could be allocated |
block,rw_lock_x_lock_count(&block->lock) | == 1 if allocation succeeded (init_mtr == mtr, or the page was not previously freed in mtr) |
block | (not allocated or initialized) otherwise |
index | in: index tree |
hint_page_no | in: hint of a good page |
file_direction | in: direction where a possible page split is made |
level | in: level where the page is placed in the tree |
mtr | in/out: mini-transaction for the allocation |
init_mtr | in/out: mini-transaction for x-latching and initializing the page |
UNIV_INTERN void btr_page_free | ( | dict_index_t * | index, |
buf_block_t * | block, | ||
mtr_t * | mtr | ||
) |
Frees a file page used in an index tree. NOTE: cannot free field external storage pages because the page must contain info on its level.
index | in: index tree |
block | in: block to be freed, x-latched |
mtr | in: mtr |
UNIV_INTERN void btr_page_free_low | ( | dict_index_t * | index, |
buf_block_t * | block, | ||
ulint | level, | ||
mtr_t * | mtr | ||
) |
Frees a file page used in an index tree. Can be used also to BLOB external storage pages, because the page level 0 can be given as an argument.
index | in: index tree |
block | in: block to be freed, x-latched |
level | in: page level |
mtr | in: mtr |
UNIV_INLINE index_id_t btr_page_get_index_id | ( | const page_t * | page | ) |
Gets the index id field of a page.
page | in: index page |
UNIV_INLINE ulint btr_page_get_level_low | ( | const page_t * | page | ) |
Gets the node level field in an index page.
page | in: index page |
Gets the next index page number.
Gets the next index page number.
< in: mini-transaction handle
page | in: index page |
mtr | in: mini-transaction handle |
Gets the previous index page number.
Gets the previous index page number.
< in: mini-transaction handle
page | in: index page |
mtr | in: mini-transaction handle |
Decides if the page should be split at the convergence point of inserts converging to left.
cursor | in: cursor at which to insert |
split_rec | out: if split recommended, the first record on upper half page, or NULL if tuple should be first |
Decides if the page should be split at the convergence point of inserts converging to right.
cursor | in: cursor at which to insert |
split_rec | out: if split recommended, the first record on upper half page, or NULL if tuple should be first |
UNIV_INTERN bool btr_page_reorganize | ( | page_cur_t * | cursor, |
dict_index_t * | index, | ||
mtr_t * | mtr | ||
) |
Reorganizes an index page.
IMPORTANT: On success, 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(). On uncompressed pages, IBUF_BITMAP_FREE is unaffected by reorganization.
true | if the operation was successful |
false | if it is a compressed page, and recompression failed |
cursor | in/out: page cursor |
index | in: the index tree of the page |
mtr | in/out: mini-transaction |
UNIV_INTERN bool btr_page_reorganize_low | ( | bool | recovery, |
ulint | z_level, | ||
page_cur_t * | cursor, | ||
dict_index_t * | index, | ||
mtr_t * | mtr | ||
) |
Reorganizes an index page.
IMPORTANT: On success, 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(). On uncompressed pages, IBUF_BITMAP_FREE is unaffected by reorganization.
true | if the operation was successful |
false | if it is a compressed page, and recompression failed |
recovery | in: true if called in recovery: locks should not be updated, i.e., there cannot exist locks on the page, and a hash index should not be dropped: it cannot exist |
z_level | in: compression level to be used if dealing with compressed page |
cursor | in/out: page cursor |
index | in: the index tree of the page |
mtr | in/out: mini-transaction |
UNIV_INTERN rec_t* btr_page_split_and_insert | ( | ulint | flags, |
btr_cur_t * | cursor, | ||
ulint ** | offsets, | ||
mem_heap_t ** | heap, | ||
const dtuple_t * | tuple, | ||
ulint | n_ext, | ||
mtr_t * | mtr | ||
) |
Splits an index page to halves and inserts the tuple. It is assumed that mtr holds an x-latch to the index tree. NOTE: the tree x-latch is released within this function! NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space (2 pages) must be guaranteed to be available before this function is called.
flags | in: undo logging and locking flags |
cursor | in: cursor at which to insert; when the function returns, the cursor is positioned on the predecessor of the inserted record |
offsets | out: offsets on inserted record |
heap | in/out: pointer to memory heap that can be emptied, or NULL |
tuple | in: tuple to insert |
n_ext | in: number of externally stored columns |
mtr | in: mtr |
UNIV_INTERN byte* btr_parse_page_reorganize | ( | byte * | ptr, |
byte * | end_ptr, | ||
dict_index_t * | index, | ||
bool | compressed, | ||
buf_block_t * | block, | ||
mtr_t * | mtr | ||
) |
Parses a redo log record of reorganizing a page.
ptr | in: buffer |
end_ptr | in: buffer end |
index | in: record descriptor |
compressed | in: true if compressed page |
block | in: page to be reorganized, or NULL |
mtr | in: mtr or NULL |
UNIV_INTERN byte* btr_parse_set_min_rec_mark | ( | byte * | ptr, |
byte * | end_ptr, | ||
ulint | comp, | ||
page_t * | page, | ||
mtr_t * | mtr | ||
) |
Parses the redo log record for setting an index record as the predefined minimum record.
ptr | in: buffer |
end_ptr | in: buffer end |
comp | in: nonzero=compact page format |
page | in: page or NULL |
mtr | in: mtr or NULL |
UNIV_INTERN dberr_t btr_root_adjust_on_import | ( | const dict_index_t * | index | ) |
Checks and adjusts the root node of a tree during IMPORT TABLESPACE.
index | in: index tree |
UNIV_INTERN page_t* btr_root_get | ( | const dict_index_t * | index, |
mtr_t * | mtr | ||
) |
Gets the root node of a tree and x-latches it.
index | in: index tree |
mtr | in: mtr |
UNIV_INTERN rec_t* btr_root_raise_and_insert | ( | ulint | flags, |
btr_cur_t * | cursor, | ||
ulint ** | offsets, | ||
mem_heap_t ** | heap, | ||
const dtuple_t * | tuple, | ||
ulint | n_ext, | ||
mtr_t * | mtr | ||
) |
Makes tree one level higher by splitting the root, and inserts the tuple. It is assumed that mtr contains an x-latch on the tree. NOTE that the operation of this function must always succeed, we cannot reverse it: therefore enough free disk space must be guaranteed to be available before this function is called.
flags | in: undo logging and locking flags |
cursor | in: cursor at which to insert: must be on the root page; when the function returns, the cursor is positioned on the predecessor of the inserted record |
offsets | out: offsets on inserted record |
heap | in/out: pointer to memory heap that can be emptied, or NULL |
tuple | in: tuple to insert |
n_ext | in: number of externally stored columns |
mtr | in: mtr |
UNIV_INTERN void btr_set_min_rec_mark | ( | rec_t * | rec, |
mtr_t * | mtr | ||
) |
Sets a record as the predefined minimum record.
rec | in/out: record |
mtr | in: mtr |
UNIV_INTERN bool btr_validate_index | ( | dict_index_t * | index, |
const trx_t * | trx | ||
) |
Checks the consistency of an index tree.
index | in: index |
trx | in: transaction or 0 |