Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Index Scan + Index Join Limit #1422

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/include/optimizer/index_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class IndexUtil {
static bool SatisfiesSortWithIndex(
catalog::CatalogAccessor *accessor, const PropertySort *prop, catalog::table_oid_t tbl_oid,
catalog::index_oid_t idx_oid,
std::unordered_map<catalog::indexkeycol_oid_t, std::vector<planner::IndexExpression>> *bounds = nullptr);
std::unordered_map<catalog::indexkeycol_oid_t, std::vector<planner::IndexExpression>> *bounds);

/**
* Checks whether a set of predicates can be satisfied with an index.
Expand Down
17 changes: 1 addition & 16 deletions src/include/optimizer/optimization_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,15 @@ class OptimizationContext {
* @param cost_upper_bound Upper cost bound
*/
OptimizationContext(OptimizerContext *context, PropertySet *required_props,
PropertySet *optional_props = new PropertySet(),
double cost_upper_bound = std::numeric_limits<double>::max())
: context_(context),
required_props_(required_props),
optional_props_(optional_props),
cost_upper_bound_(cost_upper_bound) {}

/**
* Destructor
*/
~OptimizationContext() {
delete required_props_;
delete optional_props_;
}
~OptimizationContext() { delete required_props_; }

/**
* @returns OptimizerContext
Expand All @@ -49,11 +44,6 @@ class OptimizationContext {
*/
PropertySet *GetRequiredProperties() const { return required_props_; }

/**
* @returns Properties to attempt, owned by this OptimizationContext
*/
PropertySet *GetOptionalProperties() const { return optional_props_; }

/**
* @returns Current context's upper bound cost
*/
Expand All @@ -76,11 +66,6 @@ class OptimizationContext {
*/
PropertySet *required_props_;

/**
* Required properties
*/
PropertySet *optional_props_;

/**
* Cost Upper Bound (for pruning)
*/
Expand Down
5 changes: 2 additions & 3 deletions src/include/optimizer/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@ class PropertySort : public Property {
* Constructor for PropertySort
* @param sort_columns vector of AbstractExpressions representing sort columns
* @param sort_ascending Whether each sort_column is ascending or descending
* @param required Whether the sort property needs to be satisfied at the active level
*/
PropertySort(std::vector<common::ManagedPointer<parser::AbstractExpression>> sort_columns,
std::vector<catalog::OrderByOrderingType> sort_ascending, bool required = true)
: Property(required), sort_columns_(std::move(sort_columns)), sort_ascending_(std::move(sort_ascending)) {}
std::vector<catalog::OrderByOrderingType> sort_ascending)
: sort_columns_(std::move(sort_columns)), sort_ascending_(std::move(sort_ascending)) {}

/**
* Returns the type of PropertySort
Expand Down
22 changes: 0 additions & 22 deletions src/include/optimizer/property.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ enum class PropertyType { SORT };
*/
class Property {
public:
/**
* Constructor for property
* @param required whether the property is required
*/
explicit Property(bool required) : required_(required) {}

/**
* Trivial destructor for Property
*/
Expand Down Expand Up @@ -81,22 +75,6 @@ class Property {
}
return nullptr;
}

/**
* Sets the property's required status
*/
void SetRequired(bool required) { required_ = required; }

/**
* @return whether the property is required
*/
auto GetRequired() const -> bool { return required_; }

private:
/**
* Whether the property is required
*/
bool required_{true};
};

} // namespace noisepage::optimizer
10 changes: 4 additions & 6 deletions src/optimizer/child_property_deriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void ChildPropertyDeriver::Visit(const IndexScan *op) {
// Use GetIndexOids() to get all indexes on table_alias
auto tbl_id = op->GetTableOID();
std::vector<catalog::index_oid_t> tbl_indexes = accessor_->GetIndexOids(tbl_id);
auto bounds = op->GetBounds();

auto *property_set = new PropertySet();

Expand All @@ -48,7 +49,7 @@ void ChildPropertyDeriver::Visit(const IndexScan *op) {
}

auto idx_oid = op->GetIndexOID();
if (IndexUtil::SatisfiesSortWithIndex(accessor_, sort_prop, tbl_id, idx_oid)) {
if (IndexUtil::SatisfiesSortWithIndex(accessor_, sort_prop, tbl_id, idx_oid, &bounds)) {
property_set->AddProperty(prop->Copy());
}
}
Expand Down Expand Up @@ -95,18 +96,15 @@ void ChildPropertyDeriver::Visit(UNUSED_ATTRIBUTE const Aggregate *op) {
void ChildPropertyDeriver::Visit(const Limit *op) {
// Limit fulfill the internal sort property
std::vector<PropertySet *> child_input_properties{new PropertySet()};
auto provided_prop = new PropertySet();

// Limit must satisfy output sort properties but child can attempt to satisfy sort property optionally
thepinetree marked this conversation as resolved.
Show resolved Hide resolved
if (!op->GetSortExpressions().empty()) {
const std::vector<common::ManagedPointer<parser::AbstractExpression>> &exprs = op->GetSortExpressions();
const std::vector<catalog::OrderByOrderingType> &sorts{op->GetSortAscending()};
provided_prop->AddProperty(new PropertySort(exprs, sorts));

child_input_properties[0]->AddProperty(new PropertySort(exprs, sorts, false));
child_input_properties[0]->AddProperty(new PropertySort(exprs, sorts));
}

output_.emplace_back(provided_prop, std::move(child_input_properties));
output_.emplace_back(new PropertySet(), std::move(child_input_properties));
thepinetree marked this conversation as resolved.
Show resolved Hide resolved
}

void ChildPropertyDeriver::Visit(UNUSED_ATTRIBUTE const OrderBy *op) {}
Expand Down
23 changes: 13 additions & 10 deletions src/optimizer/index_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,15 @@ bool IndexUtil::SatisfiesSortWithIndex(
if (tv_col_oid == mapped_cols[idx_ind]) {
// Column is present in both sort and index so increment both
sort_ind++, idx_ind++;
} else if (bounds != nullptr && bounds->find(lookup[mapped_cols[idx_ind]]) != bounds->end()) {
// If column is found in bounds but not sort, continue
idx_ind++;
} else if (bounds != nullptr) {
auto bound_column = bounds->find(lookup[mapped_cols[idx_ind]]);
if (bound_column != bounds->end() && bound_column->second[0] == bound_column->second[1]) {
thepinetree marked this conversation as resolved.
Show resolved Hide resolved
// If column is exactly bound but not in sort, continue
idx_ind++;
} else {
// Index column is not exactly bound so cannot use this index
return false;
}
} else {
// Column not found in index so cannot use this index
return false;
Expand Down Expand Up @@ -255,13 +261,10 @@ bool IndexUtil::CheckPredicates(

if (bounds->empty()) return false;

// Scan may be uninitialized if all bound columns are equality checks, but not all index columns are bound so is not
// exact
if (open_lows.size() == open_highs.size() && scan_type == planner::IndexScanType::Dummy) {
if (bounds->size() == open_lows.size())
scan_type = planner::IndexScanType::AscendingClosedLimit;
else
scan_type = planner::IndexScanType::AscendingClosed;
// Scan may be uninitialized if all bound columns are equality checks, but not all index columns are bound
if (scan_type == planner::IndexScanType::Dummy) {
// Cannot push down limit due to additional predicates not satisfied by index
scan_type = planner::IndexScanType::AscendingClosed;
}

NOISEPAGE_ASSERT(scan_type != planner::IndexScanType::Dummy,
Expand Down
27 changes: 3 additions & 24 deletions src/optimizer/optimizer_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,41 +287,20 @@ void OptimizeExpressionCostWithEnforcedProperty::Execute() {
for (; cur_child_idx_ < static_cast<int>(group_expr_->GetChildrenGroupsSize()); cur_child_idx_++) {
auto &i_prop = input_props[cur_child_idx_];

// Fill input properties based on required properties and optional proper ties
auto *req_input_props = new PropertySet();
auto optional_input_props = PropertySet();

for (auto *prop : i_prop->Properties()) {
if (prop->GetRequired()) {
// Required property
req_input_props->AddProperty(prop->Copy());
} else {
// Optional property
optional_input_props.AddProperty(prop->Copy());
}
}

delete input_props[cur_child_idx_];
// Only preserve required input properties, optional properties should be in child operator
input_props[cur_child_idx_] = req_input_props;

auto child_group =
context_->GetOptimizerContext()->GetMemo().GetGroupByID(group_expr_->GetChildGroupId(cur_child_idx_));

// Check whether the child group is already optimized for the required input properties
auto child_best_expr = child_group->GetBestExpression(req_input_props);
auto child_best_expr = child_group->GetBestExpression(i_prop);
if (child_best_expr != nullptr) { // Directly get back the best expr if the child group is optimized
// Only cost on required properties
// TODO(dpatra): Update costing structure for completed optional properties
cur_total_cost_ += child_best_expr->GetCost(req_input_props);
cur_total_cost_ += child_best_expr->GetCost(i_prop);
if (cur_total_cost_ > context_->GetCostUpperBound()) break;
} else if (prev_child_idx_ != cur_child_idx_) { // We haven't optimized child group
prev_child_idx_ = cur_child_idx_;
PushTask(new OptimizeExpressionCostWithEnforcedProperty(this));

auto cost_high = context_->GetCostUpperBound() - cur_total_cost_;
auto ctx = new OptimizationContext(context_->GetOptimizerContext(), req_input_props->Copy(),
optional_input_props.Copy(), cost_high);
auto ctx = new OptimizationContext(context_->GetOptimizerContext(), i_prop->Copy(), cost_high);
PushTask(new OptimizeGroup(child_group, ctx));
context_->GetOptimizerContext()->AddOptimizationContext(ctx);
return;
Expand Down
6 changes: 1 addition & 5 deletions src/optimizer/rules/implementation_rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,6 @@ void LogicalGetToPhysicalIndexScan::Transform(common::ManagedPointer<AbstractOpt
// TODO(dpatra): This assumes there will only be ONE sort property -- may want to review this later
// Try setting sort property based on properties in context
auto sort = context->GetRequiredProperties()->GetPropertyOfType(PropertyType::SORT);
// If no sort found in required properties, try optional properties
if (sort == nullptr) {
sort = context->GetOptionalProperties()->GetPropertyOfType(PropertyType::SORT);
}

bool sort_exists = sort != nullptr;
auto sort_prop = sort_exists ? sort->As<PropertySort>() : nullptr;
Expand Down Expand Up @@ -185,7 +181,7 @@ void LogicalGetToPhysicalIndexScan::Transform(common::ManagedPointer<AbstractOpt
// If sort property is not satisfied, should default to sequential scan
// NOTE: This assumes no external predicates exist; if they do, remove the limit
for (auto &index : indexes) {
if (sort_possible && IndexUtil::SatisfiesSortWithIndex(accessor, sort_prop, get->GetTableOid(), index)) {
if (sort_possible && IndexUtil::SatisfiesSortWithIndex(accessor, sort_prop, get->GetTableOid(), index, nullptr)) {
std::vector<AnnotatedExpression> preds = get->GetPredicates();
auto op = std::make_unique<OperatorNode>(
IndexScan::Make(db_oid, get->GetTableOid(), index, std::move(preds), is_update,
Expand Down