blob: a6a77019d8afd4d6934e571e7118f1e5a184d9fb [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#pragma once
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <glog/logging.h>
#include "kudu/common/columnblock.h"
#include "kudu/common/rowblock_memory.h"
#include "kudu/common/schema.h"
#include "kudu/common/types.h"
#include "kudu/gutil/macros.h"
#include "kudu/gutil/strings/stringpiece.h"
#include "kudu/util/bitmap.h"
#include "kudu/util/status.h"
namespace kudu {
class Arena;
class RowBlockRow;
class SelectedRows;
// Bit-vector representing the selection status of each row in a row block.
//
// When scanning through data, a 1 bit in the selection vector indicates that
// the given row is live and has passed all predicates.
class SelectionVector {
public:
// Construct a new vector. The bits are initially in an indeterminate state.
// Call SetAllTrue() if you require all rows to be initially selected.
explicit SelectionVector(size_t row_capacity);
// Construct a vector which shares the underlying memory of another vector,
// but only exposes up to a given prefix of the number of rows.
//
// Note that mutating the resulting bitmap may arbitrarily mutate the "extra"
// bits of the 'other' bitmap.
//
// The underlying bytes must not be deallocated or else this object will become
// invalid.
SelectionVector(SelectionVector *other, size_t prefix_rows);
// Resize the selection vector to the given number of rows.
// This size must be <= the allocated capacity.
//
// Ensures that all rows for indices < n_rows are unmodified.
void Resize(size_t n_rows);
// Zeroes out the end of the selection vector such that it will be left with
// at most 'max_rows' selected.
//
// If 'max_rows' is greater than the allocated capacity, this does nothing.
void ClearToSelectAtMost(size_t max_rows);
// Return the number of selected rows.
size_t CountSelected() const;
// Return true if any rows are selected, false if size 0.
// This is equivalent to (CountSelected() > 0), but faster.
bool AnySelected() const;
bool IsRowSelected(size_t row) const {
DCHECK_LT(row, n_rows_);
return BitmapTest(&bitmap_[0], row);
}
void SetRowSelected(size_t row) {
DCHECK_LT(row, n_rows_);
BitmapSet(&bitmap_[0], row);
}
void SetRowUnselected(size_t row) {
DCHECK_LT(row, n_rows_);
BitmapClear(&bitmap_[0], row);
}
// Finds the first selected row beginning at 'row_offset'.
//
// Returns true if at least one row is selected and writes its index to 'row';
// returns false otherwise.
bool FindFirstRowSelected(size_t row_offset, size_t* row) {
DCHECK_LT(row_offset, n_rows_);
DCHECK(row);
return BitmapFindFirstSet(&bitmap_[0], row_offset, n_rows_, row);
}
// Processes the selection vector to return a SelectedRows object which indicates
// the indices of the selected rows. The returned object refers to the memory
// of this SelectionVector and must not be retained longer than this instance.
SelectedRows GetSelectedRows() const;
uint8_t *mutable_bitmap() {
return &bitmap_[0];
}
const uint8_t *bitmap() const {
return &bitmap_[0];
}
// Set all bits in the bitmap to 1
void SetAllTrue() {
memset(&bitmap_[0], 0xff, n_bytes_);
PadExtraBitsWithZeroes();
}
// Set all bits in the bitmap to 0
void SetAllFalse() {
memset(&bitmap_[0], 0, n_bytes_);
}
size_t nrows() const { return n_rows_; }
// Copies a range of bits between two SelectionVectors.
//
// The extent of the range is designated by 'src_row_off' and 'num_rows'. It
// is copied to 'dst' at 'dst_row_off'.
//
// Note: 'dst' will be resized if the copy causes it to grow (though this is
// just a "logical" resize; no reallocation takes place).
void CopyTo(SelectionVector* dst, size_t src_row_off,
size_t dst_row_off, size_t num_rows) const {
DCHECK_GE(n_rows_, src_row_off + num_rows);
size_t new_num_rows = dst_row_off + num_rows;
if (new_num_rows > dst->nrows()) {
// This will crash if 'dst' lacks adequate capacity.
dst->Resize(new_num_rows);
}
BitmapCopy(dst->mutable_bitmap(), dst_row_off,
bitmap_.get(), src_row_off, num_rows);
}
std::string ToString() const {
return BitmapToString(&bitmap_[0], n_rows_);
}
private:
// Pads any non-byte-aligned bits at the end of the SelectionVector with zeroes.
//
// To improve performance, CountSelected() and AnySelected() evaluate the
// SelectionVector's bitmap in terms of bytes. As such, they consider all of
// the trailing bits, even if the bitmap's bit length is not byte-aligned and
// some trailing bits aren't part of the bitmap.
//
// To address this without sacrificing performance, we need to zero out all
// trailing bits at construction time, or after any operation that sets all
// bytes in bulk.
void PadExtraBitsWithZeroes() {
size_t bits_in_last_byte = n_rows_ & 7;
if (bits_in_last_byte > 0) {
BitmapChangeBits(&bitmap_[0], n_rows_, 8 - bits_in_last_byte, false);
}
}
// The number of allocated bytes in bitmap_
size_t bytes_capacity_;
size_t n_rows_;
size_t n_bytes_;
std::unique_ptr<uint8_t[]> bitmap_;
DISALLOW_COPY_AND_ASSIGN(SelectionVector);
};
bool operator==(const SelectionVector& a, const SelectionVector& b);
bool operator!=(const SelectionVector& a, const SelectionVector& b);
// A SelectionVectorView keeps track of where in the selection vector a given
// batch will start from. After processing a batch, Advance() should be called
// and the view will move forward by the appropriate amount. In this way, the
// underlying selection vector can easily be updated batch-by-batch.
class SelectionVectorView {
public:
// Constructs a new SelectionVectorView.
//
// The 'sel_vec' object must outlive this SelectionVectorView.
explicit SelectionVectorView(SelectionVector *sel_vec)
: sel_vec_(sel_vec), row_offset_(0) {
}
void Advance(size_t skip) {
DCHECK_LE(skip, sel_vec_->nrows() - row_offset_);
row_offset_ += skip;
}
void SetBit(size_t row_idx) {
DCHECK_LE(row_idx, sel_vec_->nrows() - row_offset_);
BitmapSet(sel_vec_->mutable_bitmap(), row_offset_ + row_idx);
}
void ClearBit(size_t row_idx) {
DCHECK_LE(row_idx, sel_vec_->nrows() - row_offset_);
BitmapClear(sel_vec_->mutable_bitmap(), row_offset_ + row_idx);
}
bool TestBit(size_t row_idx) {
DCHECK_LE(row_idx, sel_vec_->nrows() - row_offset_);
return BitmapTest(sel_vec_->bitmap(), row_offset_ + row_idx);
}
// Clear "nrows" bits from the supplied "offset" in the current view.
void ClearBits(size_t nrows, size_t offset = 0) {
DCHECK_LE(offset + nrows, sel_vec_->nrows() - row_offset_);
BitmapChangeBits(sel_vec_->mutable_bitmap(), row_offset_ + offset, nrows, false);
}
private:
SelectionVector* sel_vec_;
size_t row_offset_;
};
// Result type for SelectionVector::GetSelectedRows.
class SelectedRows {
public:
bool all_selected() const {
return all_selected_;
}
int num_selected() const {
return all_selected_ ? sel_vector_->nrows() : indexes_.size();
}
// Get the selected rows.
//
// NOTE: callers must check all_selected() first, and not use this
// function if all rows are selected. 'AsRowIndexes()' may be used instead.
const std::vector<uint16_t>& indexes() const {
DCHECK(!all_selected_);
return indexes_;
}
const uint8_t* bitmap() const {
return sel_vector_->bitmap();
}
// Convert this object to a simple vector of row indexes, materializing that
// vector in the case that all of the rows are selected.
std::vector<uint16_t> ToRowIndexes() && {
if (!all_selected_) {
return std::move(indexes_);
}
return CreateRowIndexes();
}
// Call F(index) for each selected index.
template<class F>
void ForEachIndex(F func) const {
if (all_selected_) {
int n_sel = num_selected();
for (int i = 0; i < n_sel; i++) {
func(i);
}
} else {
for (uint16_t i : indexes_) {
func(i);
}
}
}
private:
friend class SelectionVector;
explicit SelectedRows(const SelectionVector* sel_vector)
: sel_vector_(sel_vector),
all_selected_(true) {
}
explicit SelectedRows(const SelectionVector* sel_vector,
std::vector<uint16_t> indexes)
: sel_vector_(sel_vector),
all_selected_(false),
indexes_(std::move(indexes)) {
}
std::vector<uint16_t> CreateRowIndexes();
const SelectionVector* const sel_vector_;
const bool all_selected_;
std::vector<uint16_t> indexes_;
};
// A block of decoded rows.
// Wrapper around a buffer, which keeps the buffer's size, associated arena,
// and schema. Provides convenience accessors for indexing by row, column, etc.
//
// NOTE: TODO: We don't have any separate class for ConstRowBlock and ConstColumnBlock
// vs RowBlock and ColumnBlock. So, we use "const" in various places to either
// mean const-ness of the wrapper structure vs const-ness of the referred-to data.
// Typically in C++ this is done with separate wrapper classes for const vs non-const
// referred-to data, but that would require a lot of duplication elsewhere in the code,
// so for now, we just use convention: if you have a RowBlock or ColumnBlock parameter
// that you expect to be modifying, use a "RowBlock *param". Otherwise, use a
// "const RowBlock& param". Just because you _could_ modify the referred-to contents
// of the latter doesn't mean you _should_.
class RowBlock {
public:
// Constructs a new RowBlock.
//
// The 'schema' and 'arena' objects must outlive this RowBlock.
RowBlock(const Schema* schema, size_t nrows, RowBlockMemory* memory);
~RowBlock();
// Resize the block to the given number of rows.
// This size must be <= the the allocated capacity row_capacity().
//
// Ensures that all rows for indices < n_rows are unmodified.
void Resize(size_t n_rows);
size_t row_capacity() const {
return row_capacity_;
}
RowBlockRow row(size_t idx) const;
const Schema* schema() const { return schema_; }
Arena* arena() const { return &DCHECK_NOTNULL(memory_)->arena; }
ColumnBlock column_block(size_t col_idx) const {
return column_block(col_idx, nrows_);
}
ColumnBlock column_block(size_t col_idx, size_t nrows) const {
DCHECK_LE(nrows, nrows_);
const ColumnSchema& col_schema = schema_->column(col_idx);
uint8_t* col_data = columns_data_[col_idx];
uint8_t* nulls_bitmap = column_non_null_bitmaps_[col_idx];
return ColumnBlock(col_schema.type_info(), nulls_bitmap, col_data, nrows, memory_);
}
// Return the base pointer for the given column's data.
//
// This is used by the codegen code in preference to the "nicer" column_block APIs,
// because the codegenned code knows the column's type sizes statically, and thus
// computing the pointers by itself ends up avoiding a multiply instruction.
uint8_t* column_data_base_ptr(size_t col_idx) const {
DCHECK_LT(col_idx, columns_data_.size());
return columns_data_[col_idx];
}
// Return the number of rows in the row block. Note that this includes
// rows which were filtered out by the selection vector.
size_t nrows() const { return nrows_; }
// Zero the memory pointed to by this row block.
// This physically zeros the memory, so is not efficient - mostly useful
// from unit tests.
void ZeroMemory() {
size_t bitmap_size = BitmapSize(row_capacity_);
for (size_t i = 0; i < schema_->num_columns(); ++i) {
const ColumnSchema& col_schema = schema_->column(i);
size_t col_size = col_schema.type_info()->size() * row_capacity_;
memset(columns_data_[i], '\0', col_size);
if (column_non_null_bitmaps_[i] != NULL) {
memset(column_non_null_bitmaps_[i], '\0', bitmap_size);
}
}
}
// Return the selection vector which indicates which rows have passed
// predicates so far during evaluation of this block of rows.
//
// At the beginning of each batch, the vector is set to all 1s, and
// as predicates or deletions make rows invalid, they are set to 0s.
// After a batch has completed, only those rows with associated true
// bits in the selection vector are valid results for the scan.
SelectionVector* selection_vector() {
return &sel_vec_;
}
const SelectionVector* selection_vector() const {
return &sel_vec_;
}
// Copies a range of rows between two RowBlocks.
//
// The extent of the range is designated by 'src_row_off' and 'num_rows'. It
// is copied to 'dst' at 'dst_row_off'.
//
// Note: 'dst' will be resized if the copy causes it to grow (though this is
// just a "logical" resize; no reallocation takes place).
Status CopyTo(RowBlock* dst, size_t src_row_off,
size_t dst_row_off, size_t num_rows) const {
DCHECK_SCHEMA_EQ(*schema_, *dst->schema());
DCHECK_GE(nrows_, src_row_off + num_rows);
size_t new_num_rows = dst_row_off + num_rows;
if (new_num_rows > dst->nrows()) {
// This will crash if 'dst' lacks adequate capacity.
dst->Resize(new_num_rows);
}
for (size_t col_idx = 0; col_idx < schema_->num_columns(); col_idx++) {
ColumnBlock src_cb(column_block(col_idx));
ColumnBlock dst_cb(dst->column_block(col_idx));
RETURN_NOT_OK(src_cb.CopyTo(sel_vec_, &dst_cb,
src_row_off, dst_row_off, num_rows));
}
sel_vec_.CopyTo(&dst->sel_vec_, src_row_off, dst_row_off, num_rows);
return Status::OK();
}
private:
DISALLOW_COPY_AND_ASSIGN(RowBlock);
static size_t RowBlockSize(const Schema& schema, size_t nrows) {
size_t block_size = schema.num_columns() * sizeof(size_t);
size_t bitmap_size = BitmapSize(nrows);
for (size_t col = 0; col < schema.num_columns(); col++) {
const ColumnSchema& col_schema = schema.column(col);
block_size += nrows * col_schema.type_info()->size();
if (col_schema.is_nullable())
block_size += bitmap_size;
}
return block_size;
}
const Schema* schema_;
std::vector<uint8_t*> columns_data_;
std::vector<uint8_t*> column_non_null_bitmaps_;
// The maximum number of rows that can be stored in our allocated buffer.
size_t row_capacity_;
// The number of rows currently being processed in this block.
// nrows_ <= row_capacity_
size_t nrows_;
RowBlockMemory* memory_;
// The bitmap indicating which rows are valid in this block.
// Deleted rows or rows which have failed to pass predicates will be zeroed
// in the bitmap, and thus not returned to the end user.
SelectionVector sel_vec_;
};
// Provides an abstraction to interact with a RowBlock row.
// Example usage:
// RowBlock row_block(schema, 10, NULL);
// RowBlockRow row = row_block.row(5); // Get row 5
// void *cell_data = row.cell_ptr(3); // Get column 3 of row 5
// ...
class RowBlockRow {
public:
typedef ColumnBlock::Cell Cell;
// Constructs a new RowBlockRow.
//
// The 'row_block' object must outlive this RowBlockRow.
explicit RowBlockRow(const RowBlock* row_block = nullptr,
size_t row_index = 0)
: row_block_(row_block), row_index_(row_index) {
}
RowBlockRow* Reset(const RowBlock* row_block, size_t row_index) {
row_block_ = row_block;
row_index_ = row_index;
return this;
}
const RowBlock* row_block() const {
return row_block_;
}
size_t row_index() const {
return row_index_;
}
const Schema* schema() const {
return row_block_->schema();
}
bool is_null(size_t col_idx) const {
return column_block(col_idx).is_null(row_index_);
}
uint8_t* mutable_cell_ptr(size_t col_idx) const {
return const_cast<uint8_t*>(cell_ptr(col_idx));
}
const uint8_t* cell_ptr(size_t col_idx) const {
return column_block(col_idx).cell_ptr(row_index_);
}
const uint8_t* nullable_cell_ptr(size_t col_idx) const {
return column_block(col_idx).nullable_cell_ptr(row_index_);
}
Cell cell(size_t col_idx) const {
return row_block_->column_block(col_idx).cell(row_index_);
}
ColumnBlock column_block(size_t col_idx) const {
return row_block_->column_block(col_idx);
}
// Mark this row as unselected in the selection vector.
void SetRowUnselected() {
// TODO(todd): const-ness issues since this class holds a const RowBlock*.
// hack around this for now
SelectionVector* vec = const_cast<SelectionVector*>(row_block_->selection_vector());
vec->SetRowUnselected(row_index_);
}
#ifndef NDEBUG
void OverwriteWithPattern(StringPiece pattern) {
const Schema* schema = row_block_->schema();
for (size_t col = 0; col < schema->num_columns(); col++) {
row_block_->column_block(col).OverwriteWithPattern(row_index_, pattern);
}
}
#endif
private:
const RowBlock* row_block_;
size_t row_index_;
};
inline RowBlockRow RowBlock::row(size_t idx) const {
return RowBlockRow(this, idx);
}
} // namespace kudu