Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 0 additions & 1 deletion src/execution/index/art/art.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "duckdb/common/types/conflict_manager.hpp"
#include "duckdb/common/unordered_map.hpp"
#include "duckdb/common/vector_operations/vector_operations.hpp"
#include "duckdb/execution/expression_executor.hpp"
#include "duckdb/execution/index/art/art_key.hpp"
#include "duckdb/execution/index/art/base_leaf.hpp"
Expand Down
52 changes: 47 additions & 5 deletions src/function/table/table_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@

#include "duckdb/catalog/catalog_entry/duck_table_entry.hpp"
#include "duckdb/catalog/dependency_list.hpp"
#include "duckdb/common/enums/expression_type.hpp"
#include "duckdb/common/mutex.hpp"
#include "duckdb/common/serializer/deserializer.hpp"
#include "duckdb/common/serializer/serializer.hpp"
#include "duckdb/common/typedefs.hpp"
#include "duckdb/common/unique_ptr.hpp"
#include "duckdb/execution/index/art/art.hpp"
#include "duckdb/function/function_set.hpp"
#include "duckdb/main/attached_database.hpp"
#include "duckdb/main/client_config.hpp"
#include "duckdb/optimizer/matcher/expression_matcher.hpp"
#include "duckdb/planner/expression/bound_between_expression.hpp"
#include "duckdb/planner/expression.hpp"
#include "duckdb/planner/expression/bound_cast_expression.hpp"
#include "duckdb/planner/expression/bound_columnref_expression.hpp"
#include "duckdb/planner/expression/bound_function_expression.hpp"
#include "duckdb/planner/expression/bound_operator_expression.hpp"
#include "duckdb/planner/operator/logical_get.hpp"
#include "duckdb/storage/data_table.hpp"
#include "duckdb/storage/table/scan_state.hpp"
#include "duckdb/transaction/duck_transaction.hpp"
#include "duckdb/transaction/local_storage.hpp"
#include "duckdb/storage/storage_index.hpp"
#include "duckdb/main/client_data.hpp"
#include "duckdb/common/algorithm.hpp"
#include "duckdb/planner/filter/optional_filter.hpp"
#include "duckdb/planner/filter/in_filter.hpp"
#include "duckdb/planner/expression/bound_constant_expression.hpp"
#include "duckdb/planner/expression/bound_comparison_expression.hpp"
#include "duckdb/planner/filter/conjunction_filter.hpp"
#include "duckdb/common/types/value_map.hpp"

#include <list>

namespace duckdb {

struct TableScanLocalState : public LocalTableFunctionState {
Expand Down Expand Up @@ -484,6 +487,42 @@ vector<unique_ptr<Expression>> ExtractFilterExpressions(const ColumnDefinition &
return expressions;
}

// Recursively updates column bindings in an index expression to match the current input projection,
// even if they are the child of a function or cast
void UpdateIndexExprColumnBindings(unique_ptr<Expression> &expr, const vector<column_t> &input_column_ids,
const vector<column_t> &indexed_columns) {
if (!expr) { return; }

switch (expr->GetExpressionClass()) {
case ExpressionClass::BOUND_COLUMN_REF: {
auto &bound_column_ref_expr = expr->Cast<BoundColumnRefExpression>();

for (idx_t i=0; i < input_column_ids.size(); ++i) {
if (input_column_ids[i] == indexed_columns[0]) {
bound_column_ref_expr.binding.column_index = i;
break;
}
}
break;
}
case ExpressionClass::BOUND_FUNCTION: {
auto &func_expr = expr->Cast<BoundFunctionExpression>();
for (auto &child : func_expr.children) {
UpdateIndexExprColumnBindings(child, input_column_ids, indexed_columns);
}
break;
}
// TODO: never end up here, TableScanInitGlobal called without filters in input
// case ExpressionClass::BOUND_CAST: {
// auto &cast_expr = expr->Cast<BoundCastExpression>();
// UpdateIndexExprColumnBindings(cast_expr.child, input_column_ids, indexed_columns);
// break;
// }
default:
break;
}
}

bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInput &input, TableFilterSet &filter_set,
idx_t max_count, unsafe_vector<row_t> &row_ids) {
// FIXME: No support for index scans on compound ARTs.
Expand All @@ -500,6 +539,9 @@ bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInpu
return false;
}

// Resolve bound column references in the index_expr against the current input
UpdateIndexExprColumnBindings(index_expr, input.column_ids, indexed_columns);

// Get ART column.
auto &col = column_list.GetColumn(LogicalIndex(indexed_columns[0]));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# name: test/sql/index/art/issues/test_art_issue_view_col_binding_17290.test
# description: Test Index Scan push down against views with reordered projections
# group: [issues]

statement ok
create or replace table test as (
select
cast(unnest(range(1000)) as varchar) as x,
cast(unnest(range(2000,3000)) as varchar) as y,
cast(unnest(range(3000,4000)) as varchar) as z
);

# test simple permutation of initial table projection
statement ok
create index test_x on test(x);

statement ok
create view test_view as (select z, y, x from test);

query II
explain analyze select * from test_view where x = '525';
----
analyzed_plan <REGEX>:.*Index Scan.*

statement ok
drop index test_x;

# test columnref as a child of function
statement ok
create index test_upper_x on test(upper(x));

query II
explain analyze select * from test_view where upper(x) = '525';
----
analyzed_plan <REGEX>:.*Index Scan.*

statement ok
drop index test_upper_x;

# test columnref as child of nested cast operation (very contrived??)
# statement ok
# create index test_cast_x on test(cast(x as integer));

# query II
# explain analyze select * from test_view where cast(x as integer) = 525;
# ----
# analyzed_plan <REGEX>:.*Index Scan.*

# statement ok
# drop index test_cast_x;
Loading