@@ -493,16 +493,38 @@ bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInpu
493493 index_exprs.push_back (expr->Copy ());
494494 }
495495
496- // If this is a view, the column IDs are relative to the view projection
496+ // If this is a view, the column IDs are (may be?) relative to the view projection
497497 auto &indexed_columns = art.GetColumnIds ();
498498
499499 // Allow composite ART scans
500500 if (indexed_columns.size () != index_exprs.size ()) {
501501 return false ;
502502 }
503503
504- // ...only if each expression has a single column reference, and
505- // that single column reference positionally matches that of the index (this is guaranteed? paranoid?)
504+ // Resolve bound column references in the index_expr against the current input projection
505+ bool rewrite_index_exprs = false ;
506+ vector<column_t > index_column_to_input_pos;
507+ index_column_to_input_pos.resize (indexed_columns.size (), std::numeric_limits<idx_t >::max ());
508+
509+ // Associate indexed columns to input columns
510+ for (idx_t i = 0 ; i < indexed_columns.size (); ++i) {
511+ for (idx_t j = 0 ; j < input.column_ids .size (); ++j) {
512+ if (indexed_columns[i] == input.column_ids [j]) {
513+ rewrite_index_exprs = i != j;
514+ index_column_to_input_pos.at (i) = j;
515+ }
516+ }
517+ }
518+
519+ // Make sure that all indexed_columns were bound, or bail out
520+ for (auto col : index_column_to_input_pos) {
521+ if (col == std::numeric_limits<idx_t >::max ()) {
522+ return false ;
523+ }
524+ }
525+
526+ // Allow scan only if index expressions reference ONE column each, and that column
527+ // is associated with an indexed_column
506528 // NOTE: We do not push down multi-column filters, e.g., 42 = a + b.
507529 for (idx_t i = 0 ; i < index_exprs.size (); ++i) {
508530 unordered_set<column_t > referenced_columns;
@@ -521,70 +543,57 @@ bool TryScanIndex(ART &art, const ColumnList &column_list, TableFunctionInitInpu
521543 }
522544
523545 auto referenced_column = *referenced_columns.begin ();
546+
547+ // The column for this position matches the indexed_column ID for this position directly
524548 auto direct_match = referenced_column == indexed_columns[i];
525- auto remapped_match =
526- input.column_ids .size () > referenced_column && input.column_ids [referenced_column] == indexed_columns[i];
527549
528- if (!(direct_match || remapped_match)) {
550+ // We should know if there is a different mapping for this reference.
551+ // If there is not, it won't match, so it is not worth trying.
552+ if (!direct_match && !rewrite_index_exprs) {
529553 return false ;
530554 }
531- }
532-
533- // Resolve bound column references in the index_expr against the current input projection
534- vector<column_t > index_column_to_proj_pos;
535- index_column_to_proj_pos.resize (indexed_columns.size (), std::numeric_limits<idx_t >::max ());
536555
537- bool found_index_column_in_input = false ;
538-
539- // Associate indexed columns to input columns
540- for (idx_t i = 0 ; i < indexed_columns.size (); ++i) {
541- for (idx_t j = 0 ; j < input.column_ids .size (); ++j) {
542- if (indexed_columns[i] == input.column_ids [j]) {
543- index_column_to_proj_pos.at (i) = j;
544- found_index_column_in_input = true ;
545- }
546- }
547- }
548-
549- if (!found_index_column_in_input) {
550- return false ;
551- }
556+ auto remapped_column_id = index_column_to_input_pos[referenced_column];
557+ auto remapped_match =
558+ input.column_ids .size () > remapped_column_id && input.column_ids [remapped_column_id] == indexed_columns[i];
552559
553- for (auto col : index_column_to_proj_pos) {
554- if (col == std::numeric_limits<idx_t >::max ()) {
560+ if (!(direct_match || remapped_match)) {
555561 return false ;
556562 }
557563 }
558564
559- // Update the bound column refs within all index_exprs
560- for (auto &index_expr : index_exprs) {
561- ExpressionIterator::EnumerateExpression (index_expr, [&](Expression &expr) {
562- if (expr.GetExpressionClass () != ExpressionClass::BOUND_COLUMN_REF) {
563- return ;
564- }
565+ // If the position of the indexed_columns differs from the order of the input, remap the index expressions
566+ if (rewrite_index_exprs) {
567+ for (auto &index_expr : index_exprs) {
568+ ExpressionIterator::EnumerateExpression (index_expr, [&](Expression &expr) {
569+ if (expr.GetExpressionClass () != ExpressionClass::BOUND_COLUMN_REF) {
570+ return ;
571+ }
565572
566- auto &bound_column_ref_expr = expr.Cast <BoundColumnRefExpression>();
573+ auto &bound_column_ref_expr = expr.Cast <BoundColumnRefExpression>();
567574
568- // If the bound column references an indexed column, update it
569- for (idx_t i = 0 ; i < indexed_columns.size (); ++i) {
570- if (bound_column_ref_expr.binding .column_index == indexed_columns[i]) {
571- bound_column_ref_expr.binding .column_index = index_column_to_proj_pos[i];
572- break ;
575+ // If the bound column references an indexed column, update it
576+ for (idx_t i = 0 ; i < indexed_columns.size (); ++i) {
577+ auto remapped_index = index_column_to_input_pos[bound_column_ref_expr.binding .column_index ];
578+ if (input.column_ids [remapped_index] == indexed_columns[i]) {
579+ bound_column_ref_expr.binding .column_index = index_column_to_input_pos[i];
580+ break ;
581+ }
573582 }
574- }
575- });
583+ });
584+ }
576585 }
577586
578587 // The indexes of the filters match input.column_indexes, which are: i -> column_index.
579- // Reuse the index <-> projection mappings from index expr rebinding
588+ // Reuse the index <-> projection mappings from index expr rebinding (which are canonical even if not rewriting)
580589 vector<vector<unique_ptr<Expression>>> index_filters;
581590
582- for (idx_t i = 0 ; i < index_column_to_proj_pos .size (); ++i) {
591+ for (idx_t i = 0 ; i < index_column_to_input_pos .size (); ++i) {
583592 auto column_def = &column_list.GetColumn (LogicalIndex (indexed_columns[i]));
584- auto maybe_filter = filter_set.filters .find (index_column_to_proj_pos [i]);
593+ auto maybe_filter = filter_set.filters .find (index_column_to_input_pos [i]);
585594 if (maybe_filter != filter_set.filters .end ()) {
586595 auto filter = &maybe_filter->second ;
587- auto filter_expressions = ExtractFilterExpressions (*column_def, *filter, index_column_to_proj_pos [i]);
596+ auto filter_expressions = ExtractFilterExpressions (*column_def, *filter, index_column_to_input_pos [i]);
588597
589598 index_filters.push_back (std::move (filter_expressions));
590599 }
0 commit comments