You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by sk...@apache.org on 2019/08/19 22:25:23 UTC

[phoenix] branch 4.x-HBase-1.3 updated: PHOENIX-5390:Changing anonymous inner classes

This is an automated email from the ASF dual-hosted git repository.

skadam pushed a commit to branch 4.x-HBase-1.3
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/4.x-HBase-1.3 by this push:
     new c38fb7c  PHOENIX-5390:Changing anonymous inner classes
c38fb7c is described below

commit c38fb7c38e294358fd98c1c57b2ee661f289dd6f
Author: Neha <ne...@salesforce.com>
AuthorDate: Fri Jul 26 11:43:01 2019 -0700

    PHOENIX-5390:Changing anonymous inner classes
    
    Adding nit changes
    
    Signed-off-by: s.kadam <sk...@apache.org>
---
 .../org/apache/phoenix/execute/CorrelatePlan.java  | 194 +++++++++++----------
 .../expression/function/InvertFunction.java        |  88 +++++-----
 .../expression/function/PrefixFunction.java        | 110 ++++++------
 .../phoenix/iterate/OrderedResultIterator.java     | 100 ++++++-----
 .../apache/phoenix/optimize/QueryOptimizer.java    | 116 ++++++------
 5 files changed, 323 insertions(+), 285 deletions(-)

diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/CorrelatePlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/CorrelatePlan.java
index b5491aa..68b1505 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/CorrelatePlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/CorrelatePlan.java
@@ -56,6 +56,7 @@ public class CorrelatePlan extends DelegateQueryPlan {
     private final KeyValueSchema rhsSchema;
     private final int rhsFieldPosition;
 
+
     public CorrelatePlan(QueryPlan lhs, QueryPlan rhs, String variableId, 
             JoinType joinType, boolean isSingleValueOnly, 
             RuntimeContext runtimeContext, PTable joinedTable, 
@@ -104,99 +105,7 @@ public class CorrelatePlan extends DelegateQueryPlan {
     @Override
     public ResultIterator iterator(final ParallelScanGrouper scanGrouper, final Scan scan)
             throws SQLException {
-        return new ResultIterator() {
-            private final ValueBitSet destBitSet = ValueBitSet.newInstance(joinedSchema);
-            private final ValueBitSet lhsBitSet = ValueBitSet.newInstance(lhsSchema);
-            private final ValueBitSet rhsBitSet = 
-                    (joinType == JoinType.Semi || joinType == JoinType.Anti) ?
-                            ValueBitSet.EMPTY_VALUE_BITSET 
-                          : ValueBitSet.newInstance(rhsSchema);
-            private final ResultIterator iter = delegate.iterator(scanGrouper, scan);
-            private ResultIterator rhsIter = null;
-            private Tuple current = null;
-            private boolean closed = false;
-
-            @Override
-            public void close() throws SQLException {
-                if (!closed) {
-                    closed = true;
-                    iter.close();
-                    if (rhsIter != null) {
-                        rhsIter.close();
-                    }
-                }
-            }
-
-            @Override
-            public Tuple next() throws SQLException {
-                if (closed)
-                    return null;
-                
-                Tuple rhsCurrent = null;
-                if (rhsIter != null) {
-                    rhsCurrent = rhsIter.next();
-                    if (rhsCurrent == null) {
-                        rhsIter.close();
-                        rhsIter = null;
-                    } else if (isSingleValueOnly) {
-                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS).build().buildException();
-                    }
-                }
-                while (rhsIter == null) {
-                    current = iter.next();
-                    if (current == null) {
-                        close();
-                        return null;
-                    }
-                    runtimeContext.setCorrelateVariableValue(variableId, current);
-                    rhsIter = rhs.iterator();
-                    rhsCurrent = rhsIter.next();
-                    if ((rhsCurrent == null && (joinType == JoinType.Inner || joinType == JoinType.Semi))
-                            || (rhsCurrent != null && joinType == JoinType.Anti)) {
-                        rhsIter.close();
-                        rhsIter = null;
-                    }
-                }
-                
-                Tuple joined;
-                try {
-                    joined = rhsBitSet == ValueBitSet.EMPTY_VALUE_BITSET ?
-                            current : TupleProjector.mergeProjectedValue(
-                                    convertLhs(current), destBitSet,
-                                    rhsCurrent, rhsBitSet, rhsFieldPosition, true);
-                } catch (IOException e) {
-                    throw new SQLException(e);
-                }
-                                
-                if ((joinType == JoinType.Semi || rhsCurrent == null) && rhsIter != null) {
-                    rhsIter.close();
-                    rhsIter = null;
-                }
-                
-                return joined;
-            }
-
-            @Override
-            public void explain(List<String> planSteps) {
-            }
-            
-            private ProjectedValueTuple convertLhs(Tuple lhs) throws IOException {
-                ProjectedValueTuple t;
-                if (lhs instanceof ProjectedValueTuple) {
-                    t = (ProjectedValueTuple) lhs;
-                } else {
-                    ImmutableBytesWritable ptr = getContext().getTempPtr();
-                    TupleProjector.decodeProjectedValue(lhs, ptr);
-                    lhsBitSet.clear();
-                    lhsBitSet.or(ptr);
-                    int bitSetLen = lhsBitSet.getEstimatedLength();
-                    t = new ProjectedValueTuple(lhs, lhs.getValue(0).getTimestamp(), 
-                            ptr.get(), ptr.getOffset(), ptr.getLength(), bitSetLen);
-
-                }
-                return t;
-            }
-        };
+        return new CorrelateResultIterator(scanGrouper, scan) ;
     }
 
     @Override
@@ -227,4 +136,103 @@ public class CorrelatePlan extends DelegateQueryPlan {
         return cost.plus(lhsCost).plus(rhs.getCost());
     }
 
+    private class CorrelateResultIterator implements ResultIterator {
+        private final ValueBitSet destBitSet = ValueBitSet.newInstance(joinedSchema);
+        private final ValueBitSet lhsBitSet = ValueBitSet.newInstance(lhsSchema);
+        private final ValueBitSet rhsBitSet =
+                (joinType == JoinType.Semi || joinType == JoinType.Anti) ?
+                        ValueBitSet.EMPTY_VALUE_BITSET
+                        : ValueBitSet.newInstance(rhsSchema);
+        private final ResultIterator iter;
+        private ResultIterator rhsIter = null;
+        private Tuple current = null;
+        private boolean closed = false;
+
+        private CorrelateResultIterator(ParallelScanGrouper scanGrouper, Scan scan)
+                throws SQLException {
+            iter = delegate.iterator(scanGrouper, scan);
+        }
+
+        @Override
+        public void close() throws SQLException {
+            if (!closed) {
+                closed = true;
+                iter.close();
+                if (rhsIter != null) {
+                    rhsIter.close();
+                }
+            }
+        }
+
+        @Override
+        public Tuple next() throws SQLException {
+            if (closed)
+                return null;
+
+            Tuple rhsCurrent = null;
+            if (rhsIter != null) {
+                rhsCurrent = rhsIter.next();
+                if (rhsCurrent == null) {
+                    rhsIter.close();
+                    rhsIter = null;
+                } else if (isSingleValueOnly) {
+                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.SINGLE_ROW_SUBQUERY_RETURNS_MULTIPLE_ROWS)
+                            .build().buildException();
+                }
+            }
+            while (rhsIter == null) {
+                current = iter.next();
+                if (current == null) {
+                    close();
+                    return null;
+                }
+                runtimeContext.setCorrelateVariableValue(variableId, current);
+                rhsIter = rhs.iterator();
+                rhsCurrent = rhsIter.next();
+                if ((rhsCurrent == null && (joinType == JoinType.Inner || joinType == JoinType.Semi))
+                        || (rhsCurrent != null && joinType == JoinType.Anti)) {
+                    rhsIter.close();
+                    rhsIter = null;
+                }
+            }
+
+            Tuple joined;
+            try {
+                joined = rhsBitSet == ValueBitSet.EMPTY_VALUE_BITSET ?
+                        current : TupleProjector.mergeProjectedValue(
+                        convertLhs(current), destBitSet,
+                        rhsCurrent, rhsBitSet, rhsFieldPosition, true);
+            } catch (IOException e) {
+                throw new SQLException(e);
+            }
+
+            if ((joinType == JoinType.Semi || rhsCurrent == null) && rhsIter != null) {
+                rhsIter.close();
+                rhsIter = null;
+            }
+
+            return joined;
+        }
+
+        @Override
+        public void explain(List<String> planSteps) { }
+
+        private ProjectedValueTuple convertLhs(Tuple lhs) throws IOException {
+            ProjectedValueTuple tuple;
+            if (lhs instanceof ProjectedValueTuple) {
+                tuple = (ProjectedValueTuple) lhs;
+            } else {
+                ImmutableBytesWritable ptr = getContext().getTempPtr();
+                TupleProjector.decodeProjectedValue(lhs, ptr);
+                lhsBitSet.clear();
+                lhsBitSet.or(ptr);
+                int bitSetLen = lhsBitSet.getEstimatedLength();
+                tuple = new ProjectedValueTuple(lhs, lhs.getValue(0).getTimestamp(),
+                        ptr.get(), ptr.getOffset(), ptr.getLength(), bitSetLen);
+
+            }
+            return tuple;
+        }
+    }
+
 }
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/InvertFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/InvertFunction.java
index 8ef5914..038221d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/InvertFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/InvertFunction.java
@@ -91,46 +91,7 @@ public class InvertFunction extends ScalarFunction {
      */
     @Override
     public KeyPart newKeyPart(final KeyPart childPart) {
-        return new KeyPart() {
-
-            @Override
-            public KeyRange getKeyRange(CompareOp op, Expression rhs) {
-                KeyRange range = childPart.getKeyRange(op, rhs);
-                byte[] lower = range.getLowerRange();
-                if (!range.lowerUnbound()) {
-                    lower = SortOrder.invert(lower, 0, lower.length);
-                }
-                byte[] upper;
-                if (range.isSingleKey()) {
-                    upper = lower;
-                } else {
-                    upper = range.getUpperRange();
-                    if (!range.upperUnbound()) {
-                        upper = SortOrder.invert(upper, 0, upper.length);
-                    }
-                }
-                range = KeyRange.getKeyRange(lower, range.isLowerInclusive(), upper, range.isUpperInclusive());
-                if (getColumn().getSortOrder() == SortOrder.DESC) {
-                    range = range.invert();
-                }
-                return range;
-            }
-
-            @Override
-            public List<Expression> getExtractNodes() {
-                return childPart.getExtractNodes();
-            }
-
-            @Override
-            public PColumn getColumn() {
-                return childPart.getColumn();
-            }
-
-            @Override
-            public PTable getTable() {
-                return childPart.getTable();
-            }
-        };
+        return new InvertKeyPart(childPart);
     }
 
     @Override
@@ -141,4 +102,51 @@ public class InvertFunction extends ScalarFunction {
     private Expression getChildExpression() {
         return children.get(0);
     }
+
+    private static class InvertKeyPart implements KeyPart {
+
+        private final KeyPart childPart;
+
+        public InvertKeyPart(KeyPart childPart) {
+            this.childPart = childPart;
+        }
+
+        @Override
+        public KeyRange getKeyRange(CompareOp op, Expression rhs) {
+            KeyRange range = childPart.getKeyRange(op, rhs);
+            byte[] lower = range.getLowerRange();
+            if (!range.lowerUnbound()) {
+                lower = SortOrder.invert(lower, 0, lower.length);
+            }
+            byte[] upper;
+            if (range.isSingleKey()) {
+                upper = lower;
+            } else {
+                upper = range.getUpperRange();
+                if (!range.upperUnbound()) {
+                    upper = SortOrder.invert(upper, 0, upper.length);
+                }
+            }
+            range = KeyRange.getKeyRange(lower, range.isLowerInclusive(), upper, range.isUpperInclusive());
+            if (getColumn().getSortOrder() == SortOrder.DESC) {
+                range = range.invert();
+            }
+            return range;
+        }
+
+        @Override
+        public List<Expression> getExtractNodes() {
+            return childPart.getExtractNodes();
+        }
+
+        @Override
+        public PColumn getColumn() {
+            return childPart.getColumn();
+        }
+
+        @Override
+        public PTable getTable() {
+            return childPart.getTable();
+        }
+    }
 }
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/PrefixFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/PrefixFunction.java
index ff3e74d..b09c14d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/PrefixFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/PrefixFunction.java
@@ -21,7 +21,6 @@ package org.apache.phoenix.expression.function;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
-
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.phoenix.compile.KeyPart;
 import org.apache.phoenix.expression.Expression;
@@ -34,8 +33,7 @@ import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.util.ByteUtil;
 
 abstract public class PrefixFunction extends ScalarFunction {
-    public PrefixFunction() {
-    }
+    public PrefixFunction() { }
 
     public PrefixFunction(List<Expression> children) {
         super(children);
@@ -52,26 +50,36 @@ abstract public class PrefixFunction extends ScalarFunction {
 
     @Override
     public KeyPart newKeyPart(final KeyPart childPart) {
-        return new KeyPart() {
-            private final List<Expression> extractNodes = extractNode() ? Collections.<Expression>singletonList(PrefixFunction.this) : Collections.<Expression>emptyList();
+        return new PrefixKeyPart(childPart);
+    }
 
-            @Override
-            public PColumn getColumn() {
-                return childPart.getColumn();
-            }
+    private class PrefixKeyPart implements KeyPart {
+        private final List<Expression> extractNodes = extractNode() ?
+                Collections.<Expression>singletonList(PrefixFunction.this)
+                : Collections.<Expression>emptyList();
+        private final KeyPart childPart;
 
-            @Override
-            public List<Expression> getExtractNodes() {
-                return extractNodes;
-            }
+        PrefixKeyPart(KeyPart childPart) {
+            this.childPart = childPart;
+        }
+
+        @Override
+        public PColumn getColumn() {
+            return childPart.getColumn();
+        }
+
+        @Override
+        public List<Expression> getExtractNodes() {
+            return extractNodes;
+        }
 
-            @Override
-            public KeyRange getKeyRange(CompareOp op, Expression rhs) {
-                byte[] lowerRange = KeyRange.UNBOUND;
-                byte[] upperRange = KeyRange.UNBOUND;
-                boolean lowerInclusive = true;
-                PDataType type = getColumn().getDataType();
-                switch (op) {
+        @Override
+        public KeyRange getKeyRange(CompareOp op, Expression rhs) {
+            byte[] lowerRange = KeyRange.UNBOUND;
+            byte[] upperRange = KeyRange.UNBOUND;
+            boolean lowerInclusive = true;
+            PDataType type = getColumn().getDataType();
+            switch (op) {
                 case EQUAL:
                     lowerRange = evaluateExpression(rhs);
                     upperRange = ByteUtil.nextKey(lowerRange);
@@ -85,44 +93,42 @@ abstract public class PrefixFunction extends ScalarFunction {
                     break;
                 default:
                     return childPart.getKeyRange(op, rhs);
-                }
-                PColumn column = getColumn();
-                Integer length = column.getMaxLength();
-                if (type.isFixedWidth()) {
-                    if (length != null) { // Sanity check - shouldn't be necessary
-                        // Don't pad based on current sort order, but instead use our
-                        // minimum byte as otherwise we'll end up skipping rows in
-                        // the case of descending, since rows with more padding appear
-                        // *after* rows with no padding.
-                        if (lowerRange != KeyRange.UNBOUND) {
-                            lowerRange = type.pad(lowerRange, length, SortOrder.ASC);
-                        }
-                        if (upperRange != KeyRange.UNBOUND) {
-                            upperRange = type.pad(upperRange, length, SortOrder.ASC);
-                        }
-                    }
-                } else if (column.getSortOrder() == SortOrder.DESC && getTable().rowKeyOrderOptimizable()) {
-                    // Append a zero byte if descending since a \xFF byte will be appended to the lowerRange
-                    // causing rows to be skipped that should be included. For example, with rows 'ab', 'a',
-                    // a lowerRange of 'a\xFF' would skip 'ab', while 'a\x00\xFF' would not.
+            }
+            PColumn column = getColumn();
+            Integer length = column.getMaxLength();
+            if (type.isFixedWidth()) {
+                if (length != null) { // Sanity check - shouldn't be necessary
+                    // Don't pad based on current sort order, but instead use our
+                    // minimum byte as otherwise we'll end up skipping rows in
+                    // the case of descending, since rows with more padding appear
+                    // *after* rows with no padding.
                     if (lowerRange != KeyRange.UNBOUND) {
-                        lowerRange = Arrays.copyOf(lowerRange, lowerRange.length+1);
-                        lowerRange[lowerRange.length-1] = QueryConstants.SEPARATOR_BYTE;
+                        lowerRange = type.pad(lowerRange, length, SortOrder.ASC);
+                    }
+                    if (upperRange != KeyRange.UNBOUND) {
+                        upperRange = type.pad(upperRange, length, SortOrder.ASC);
                     }
                 }
-                KeyRange range = KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, false);
-                if (column.getSortOrder() == SortOrder.DESC) {
-                    range = range.invert();
+            } else if (column.getSortOrder() == SortOrder.DESC && getTable().rowKeyOrderOptimizable()) {
+                // Append a zero byte if descending since a \xFF byte will be appended to the lowerRange
+                // causing rows to be skipped that should be included. For example, with rows 'ab', 'a',
+                // a lowerRange of 'a\xFF' would skip 'ab', while 'a\x00\xFF' would not.
+                if (lowerRange != KeyRange.UNBOUND) {
+                    lowerRange = Arrays.copyOf(lowerRange, lowerRange.length+1);
+                    lowerRange[lowerRange.length-1] = QueryConstants.SEPARATOR_BYTE;
                 }
-                return range;
             }
-
-            @Override
-            public PTable getTable() {
-                return childPart.getTable();
+            KeyRange range = KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, false);
+            if (column.getSortOrder() == SortOrder.DESC) {
+                range = range.invert();
             }
-        };
-    }
+            return range;
+        }
 
+        @Override
+        public PTable getTable() {
+            return childPart.getTable();
+        }
+    }
 
 }
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
index 212410c..5430226 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/OrderedResultIterator.java
@@ -257,52 +257,7 @@ public class OrderedResultIterator implements PeekingResultIterator {
             final SizeAwareQueue<ResultEntry> queueEntries =
                     PhoenixQueues.newResultEntrySortedQueue(comparator, limit, spoolingEnabled,
                         thresholdBytes);
-            resultIterator = new PeekingResultIterator() {
-                int count = 0;
-
-                @Override
-                public Tuple next() throws SQLException {
-                    ResultEntry entry = queueEntries.poll();
-                    while (entry != null && offset != null && count < offset) {
-                        count++;
-                        if (entry.getResult() == null) { return null; }
-                        entry = queueEntries.poll();
-                    }
-                    if (entry == null || (limit != null && count++ > limit)) {
-                        resultIterator.close();
-                        resultIterator = PeekingResultIterator.EMPTY_ITERATOR;
-                        return null;
-                    }
-                    return entry.getResult();
-                }
-                
-                @Override
-                public Tuple peek() throws SQLException {
-                    ResultEntry entry = queueEntries.peek();
-                    while (entry != null && offset != null && count < offset) {
-                        entry = queueEntries.poll();
-                        count++;
-                        if (entry == null) { return null; }
-                    }
-                    if (limit != null && count > limit) { return null; }
-                    entry = queueEntries.peek();
-                    if (entry == null) { return null; }
-                    return entry.getResult();
-                }
-
-                @Override
-                public void explain(List<String> planSteps) {
-                }
-                
-                @Override
-                public void close() throws SQLException {
-                    try {
-                      queueEntries.close();
-                    } catch (IOException e) {
-                      throw new SQLException(e);
-                    }
-                }
-            };
+            resultIterator = new RecordPeekingResultIterator(queueEntries);
             for (Tuple result = delegate.next(); result != null; result = delegate.next()) {
                 int pos = 0;
                 ImmutableBytesWritable[] sortKeys = new ImmutableBytesWritable[numSortKeys];
@@ -356,4 +311,57 @@ public class OrderedResultIterator implements PeekingResultIterator {
                 + ", resultIterator=" + resultIterator + ", byteSize="
                 + byteSize + "]";
     }
+
+    private class RecordPeekingResultIterator implements PeekingResultIterator {
+        int count = 0;
+
+        private SizeAwareQueue<ResultEntry> queueEntries;
+
+        RecordPeekingResultIterator(SizeAwareQueue<ResultEntry> queueEntries){
+            this.queueEntries = queueEntries;
+        }
+
+        @Override
+        public Tuple next() throws SQLException {
+            ResultEntry entry = queueEntries.poll();
+            while (entry != null && offset != null && count < offset) {
+                count++;
+                if (entry.getResult() == null) { return null; }
+                entry = queueEntries.poll();
+            }
+            if (entry == null || (limit != null && count++ > limit)) {
+                resultIterator.close();
+                resultIterator = PeekingResultIterator.EMPTY_ITERATOR;
+                return null;
+            }
+            return entry.getResult();
+        }
+
+        @Override
+        public Tuple peek() throws SQLException {
+            ResultEntry entry = queueEntries.peek();
+            while (entry != null && offset != null && count < offset) {
+                entry = queueEntries.poll();
+                count++;
+                if (entry == null) { return null; }
+            }
+            if (limit != null && count > limit) { return null; }
+            entry = queueEntries.peek();
+            if (entry == null) { return null; }
+            return entry.getResult();
+        }
+
+        @Override
+        public void explain(List<String> planSteps) {
+        }
+
+        @Override
+        public void close() throws SQLException {
+            try {
+                queueEntries.close();
+            } catch (IOException e) {
+                throw new SQLException(e);
+            }
+        }
+    }
 }
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
index 45c7e7d..7cad7ec 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
@@ -590,71 +590,79 @@ public class QueryOptimizer {
             final PhoenixConnection connection, final ColumnResolver resolver,
             final SelectStatement select, final Map<TableRef, TableRef> replacement) throws SQLException {
         TableNode from = select.getFrom();
-        TableNode newFrom = from.accept(new TableNodeVisitor<TableNode>() {
-            private TableRef resolveTable(String alias, TableName name) throws SQLException {
-                if (alias != null)
-                    return resolver.resolveTable(null, alias);
+        TableNode newFrom = from.accept(new QueryOptimizerTableNode(resolver, replacement));
+        if (from == newFrom) {
+            return select;
+        }
 
-                return resolver.resolveTable(name.getSchemaName(), name.getTableName());
-            }
+        SelectStatement indexSelect = IndexStatementRewriter.translate(FACTORY.select(select, newFrom), resolver, replacement);
+        for (TableRef indexTableRef : replacement.values()) {
+            // replace expressions with corresponding matching columns for functional indexes
+            indexSelect = ParseNodeRewriter.rewrite(indexSelect, new IndexExpressionParseNodeRewriter(indexTableRef.getTable(), indexTableRef.getTableAlias(), connection, indexSelect.getUdfParseNodes()));
+        }
 
-            private TableName getReplacedTableName(TableRef tableRef) {
-                String schemaName = tableRef.getTable().getSchemaName().getString();
-                return TableName.create(schemaName.length() == 0 ? null : schemaName, tableRef.getTable().getTableName().getString());
-            }
+        return indexSelect;
+    }
+    private static class QueryOptimizerTableNode implements TableNodeVisitor<TableNode> {
+        private final ColumnResolver resolver;
+        private final Map<TableRef, TableRef> replacement;
 
-            @Override
-            public TableNode visit(BindTableNode boundTableNode) throws SQLException {
-                TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
-                TableRef replaceRef = replacement.get(tableRef);
-                if (replaceRef == null)
-                    return boundTableNode;
-
-                String alias = boundTableNode.getAlias();
-                return FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef));
-            }
+        QueryOptimizerTableNode (ColumnResolver resolver, final Map<TableRef, TableRef> replacement){
+            this.resolver = resolver;
+            this.replacement = replacement;
+        }
 
-            @Override
-            public TableNode visit(JoinTableNode joinNode) throws SQLException {
-                TableNode lhs = joinNode.getLHS();
-                TableNode rhs = joinNode.getRHS();
-                TableNode lhsReplace = lhs.accept(this);
-                TableNode rhsReplace = rhs.accept(this);
-                if (lhs == lhsReplace && rhs == rhsReplace)
-                    return joinNode;
-
-                return FACTORY.join(joinNode.getType(), lhsReplace, rhsReplace, joinNode.getOnNode(), joinNode.isSingleValueOnly());
-            }
+        private TableRef resolveTable(String alias, TableName name) throws SQLException {
+            if (alias != null)
+                return resolver.resolveTable(null, alias);
 
-            @Override
-            public TableNode visit(NamedTableNode namedTableNode)
-                    throws SQLException {
-                TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
-                TableRef replaceRef = replacement.get(tableRef);
-                if (replaceRef == null)
-                    return namedTableNode;
+            return resolver.resolveTable(name.getSchemaName(), name.getTableName());
+        }
 
-                String alias = namedTableNode.getAlias();
-                return FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef), namedTableNode.getDynamicColumns(), namedTableNode.getTableSamplingRate());
-            }
+        private TableName getReplacedTableName(TableRef tableRef) {
+            String schemaName = tableRef.getTable().getSchemaName().getString();
+            return TableName.create(schemaName.length() == 0 ? null : schemaName, tableRef.getTable().getTableName().getString());
+        }
 
-            @Override
-            public TableNode visit(DerivedTableNode subselectNode)
-                    throws SQLException {
-                return subselectNode;
-            }
-        });
+        @Override
+        public TableNode visit(BindTableNode boundTableNode) throws SQLException {
+            TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
+            TableRef replaceRef = replacement.get(tableRef);
+            if (replaceRef == null)
+                return boundTableNode;
+
+            String alias = boundTableNode.getAlias();
+            return FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef));
+        }
 
-        if (from == newFrom) {
-            return select;
+        @Override
+        public TableNode visit(JoinTableNode joinNode) throws SQLException {
+            TableNode lhs = joinNode.getLHS();
+            TableNode rhs = joinNode.getRHS();
+            TableNode lhsReplace = lhs.accept(this);
+            TableNode rhsReplace = rhs.accept(this);
+            if (lhs == lhsReplace && rhs == rhsReplace)
+                return joinNode;
+
+            return FACTORY.join(joinNode.getType(), lhsReplace, rhsReplace, joinNode.getOnNode(), joinNode.isSingleValueOnly());
         }
 
-        SelectStatement indexSelect = IndexStatementRewriter.translate(FACTORY.select(select, newFrom), resolver, replacement);
-        for (TableRef indexTableRef : replacement.values()) {
-            // replace expressions with corresponding matching columns for functional indexes
-            indexSelect = ParseNodeRewriter.rewrite(indexSelect, new IndexExpressionParseNodeRewriter(indexTableRef.getTable(), indexTableRef.getTableAlias(), connection, indexSelect.getUdfParseNodes()));
+        @Override
+        public TableNode visit(NamedTableNode namedTableNode)
+                    throws SQLException {
+            TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
+            TableRef replaceRef = replacement.get(tableRef);
+            if (replaceRef == null)
+                return namedTableNode;
+
+            String alias = namedTableNode.getAlias();
+            return FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef), namedTableNode.getDynamicColumns(), namedTableNode.getTableSamplingRate());
         }
 
-        return indexSelect;
+        @Override
+        public TableNode visit(DerivedTableNode subselectNode)
+                    throws SQLException {
+            return subselectNode;
+        }
     }
 }