private PlanNode planTableScan(TableScanNode node, Expression predicate, Context context) { DomainTranslator.ExtractionResult decomposedPredicate = DomainTranslator.fromPredicate(metadata, session, predicate, symbolAllocator.getTypes()); TupleDomain<ColumnHandle> simplifiedConstraint = decomposedPredicate .getTupleDomain() .transform(node.getAssignments()::get) .intersect(node.getCurrentConstraint()); checkState(node.getOutputSymbols().containsAll(context.getLookupSymbols())); Set<ColumnHandle> lookupColumns = context .getLookupSymbols() .stream() .map(node.getAssignments()::get) .collect(toImmutableSet()); Set<ColumnHandle> outputColumns = node.getOutputSymbols() .stream() .map(node.getAssignments()::get) .collect(toImmutableSet()); Optional<ResolvedIndex> optionalResolvedIndex = metadata.resolveIndex( session, node.getTable(), lookupColumns, outputColumns, simplifiedConstraint); if (!optionalResolvedIndex.isPresent()) { // No index available, so give up by returning something return node; } ResolvedIndex resolvedIndex = optionalResolvedIndex.get(); Map<ColumnHandle, Symbol> inverseAssignments = ImmutableBiMap.copyOf(node.getAssignments()).inverse(); PlanNode source = new IndexSourceNode( idAllocator.getNextId(), resolvedIndex.getIndexHandle(), node.getTable(), node.getLayout(), context.getLookupSymbols(), node.getOutputSymbols(), node.getAssignments(), simplifiedConstraint); Expression resultingPredicate = combineConjuncts( DomainTranslator.toPredicate( resolvedIndex.getUnresolvedTupleDomain().transform(inverseAssignments::get)), decomposedPredicate.getRemainingExpression()); if (!resultingPredicate.equals(TRUE_LITERAL)) { // todo it is likely we end up with redundant filters here because the predicate push down // has already been run... the fix is to run predicate push down again source = new FilterNode(idAllocator.getNextId(), source, resultingPredicate); } context.markSuccess(); return source; }