Skip to content

Commit 5bfe7fa

Browse files
authored
Fix index resolution when querying table with index via view #5
Fix index resolution when querying table with index via view (duckdb#18319)
2 parents 4630656 + b3adcd7 commit 5bfe7fa

3 files changed

Lines changed: 97 additions & 6 deletions

File tree

src/execution/index/art/art.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "duckdb/common/types/conflict_manager.hpp"
44
#include "duckdb/common/unordered_map.hpp"
5-
#include "duckdb/common/vector_operations/vector_operations.hpp"
65
#include "duckdb/execution/expression_executor.hpp"
76
#include "duckdb/execution/index/art/art_key.hpp"
87
#include "duckdb/execution/index/art/base_leaf.hpp"

src/function/table/table_scan.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,35 @@
22

33
#include "duckdb/catalog/catalog_entry/duck_table_entry.hpp"
44
#include "duckdb/catalog/dependency_list.hpp"
5+
#include "duckdb/common/enums/expression_type.hpp"
56
#include "duckdb/common/mutex.hpp"
67
#include "duckdb/common/serializer/deserializer.hpp"
78
#include "duckdb/common/serializer/serializer.hpp"
9+
#include "duckdb/common/typedefs.hpp"
10+
#include "duckdb/common/unique_ptr.hpp"
811
#include "duckdb/execution/index/art/art.hpp"
912
#include "duckdb/function/function_set.hpp"
1013
#include "duckdb/main/attached_database.hpp"
1114
#include "duckdb/main/client_config.hpp"
12-
#include "duckdb/optimizer/matcher/expression_matcher.hpp"
13-
#include "duckdb/planner/expression/bound_between_expression.hpp"
15+
#include "duckdb/planner/expression.hpp"
16+
#include "duckdb/planner/expression/bound_cast_expression.hpp"
17+
#include "duckdb/planner/expression/bound_columnref_expression.hpp"
18+
#include "duckdb/planner/expression/bound_function_expression.hpp"
19+
#include "duckdb/planner/expression/bound_operator_expression.hpp"
1420
#include "duckdb/planner/operator/logical_get.hpp"
1521
#include "duckdb/storage/data_table.hpp"
1622
#include "duckdb/storage/table/scan_state.hpp"
1723
#include "duckdb/transaction/duck_transaction.hpp"
1824
#include "duckdb/transaction/local_storage.hpp"
1925
#include "duckdb/storage/storage_index.hpp"
2026
#include "duckdb/main/client_data.hpp"
21-
#include "duckdb/common/algorithm.hpp"
2227
#include "duckdb/planner/filter/optional_filter.hpp"
2328
#include "duckdb/planner/filter/in_filter.hpp"
2429
#include "duckdb/planner/expression/bound_constant_expression.hpp"
2530
#include "duckdb/planner/expression/bound_comparison_expression.hpp"
2631
#include "duckdb/planner/filter/conjunction_filter.hpp"
2732
#include "duckdb/common/types/value_map.hpp"
2833

29-
#include <list>
30-
3134
namespace duckdb {
3235

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

490+
// Recursively updates column bindings in an index expression to match the current input projection,
491+
// even if they are the child of a function or cast
492+
void UpdateIndexExprColumnBindings(unique_ptr<Expression> &expr, const vector<column_t> &input_column_ids,
493+
const vector<column_t> &indexed_columns) {
494+
if (!expr) { return; }
495+
496+
switch (expr->GetExpressionClass()) {
497+
case ExpressionClass::BOUND_COLUMN_REF: {
498+
auto &bound_column_ref_expr = expr->Cast<BoundColumnRefExpression>();
499+
500+
for (idx_t i=0; i < input_column_ids.size(); ++i) {
501+
if (input_column_ids[i] == indexed_columns[0]) {
502+
bound_column_ref_expr.binding.column_index = i;
503+
break;
504+
}
505+
}
506+
break;
507+
}
508+
case ExpressionClass::BOUND_FUNCTION: {
509+
auto &func_expr = expr->Cast<BoundFunctionExpression>();
510+
for (auto &child : func_expr.children) {
511+
UpdateIndexExprColumnBindings(child, input_column_ids, indexed_columns);
512+
}
513+
break;
514+
}
515+
// TODO: never end up here, TableScanInitGlobal called without filters in input
516+
// case ExpressionClass::BOUND_CAST: {
517+
// auto &cast_expr = expr->Cast<BoundCastExpression>();
518+
// UpdateIndexExprColumnBindings(cast_expr.child, input_column_ids, indexed_columns);
519+
// break;
520+
// }
521+
default:
522+
break;
523+
}
524+
}
525+
487526
bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInput &input, TableFilterSet &filter_set,
488527
idx_t max_count, unsafe_vector<row_t> &row_ids) {
489528
// FIXME: No support for index scans on compound ARTs.
@@ -500,6 +539,9 @@ bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInpu
500539
return false;
501540
}
502541

542+
// Resolve bound column references in the index_expr against the current input
543+
UpdateIndexExprColumnBindings(index_expr, input.column_ids, indexed_columns);
544+
503545
// Get ART column.
504546
auto &col = column_list.GetColumn(LogicalIndex(indexed_columns[0]));
505547

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# name: test/sql/index/art/issues/test_art_issue_view_col_binding_17290.test
2+
# description: Test Index Scan push down against views with reordered projections
3+
# group: [issues]
4+
5+
statement ok
6+
create or replace table test as (
7+
select
8+
cast(unnest(range(1000)) as varchar) as x,
9+
cast(unnest(range(2000,3000)) as varchar) as y,
10+
cast(unnest(range(3000,4000)) as varchar) as z
11+
);
12+
13+
# test simple permutation of initial table projection
14+
statement ok
15+
create index test_x on test(x);
16+
17+
statement ok
18+
create view test_view as (select z, y, x from test);
19+
20+
query II
21+
explain analyze select * from test_view where x = '525';
22+
----
23+
analyzed_plan <REGEX>:.*Index Scan.*
24+
25+
statement ok
26+
drop index test_x;
27+
28+
# test columnref as a child of function
29+
statement ok
30+
create index test_upper_x on test(upper(x));
31+
32+
query II
33+
explain analyze select * from test_view where upper(x) = '525';
34+
----
35+
analyzed_plan <REGEX>:.*Index Scan.*
36+
37+
statement ok
38+
drop index test_upper_x;
39+
40+
# test columnref as child of nested cast operation (very contrived??)
41+
# statement ok
42+
# create index test_cast_x on test(cast(x as integer));
43+
44+
# query II
45+
# explain analyze select * from test_view where cast(x as integer) = 525;
46+
# ----
47+
# analyzed_plan <REGEX>:.*Index Scan.*
48+
49+
# statement ok
50+
# drop index test_cast_x;

0 commit comments

Comments
 (0)