org.apache.phoenix.execute.LookupJoinPlan Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.execute;
import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.OFFSET_ON_SERVER;
import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.SEMI_LEFT_TABLE_NAME;
import static org.apache.phoenix.util.NumberUtil.add;
import static org.apache.phoenix.util.NumberUtil.getMin;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.phoenix.compile.ExplainPlan;
import org.apache.phoenix.compile.QueryPlan;
import org.apache.phoenix.compile.RowProjector;
import org.apache.phoenix.compile.StatementContext;
import org.apache.phoenix.coprocessor.BaseScannerRegionObserver;
import org.apache.phoenix.filter.FilterFactory;
import org.apache.phoenix.index.IndexMaintainer;
import org.apache.phoenix.iterate.LimitingResultIterator;
import org.apache.phoenix.iterate.OffsetResultIterator;
import org.apache.phoenix.iterate.ParallelScanGrouper;
import org.apache.phoenix.iterate.ResultIterator;
import org.apache.phoenix.iterate.SequenceResultIterator;
import org.apache.phoenix.parse.FilterableStatement;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.TableRef;
import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.SchemaUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
public class LookupJoinPlan extends DelegateQueryPlan {
private static final Log LOG = LogFactory.getLog(LookupJoinPlan.class);
private final QueryPlan plan;
private final Set tableRefs;
private Long estimatedRows;
private Long estimatedBytes;
private Long estimateInfoTs;
private boolean explainPlanCalled;
public LookupJoinPlan(QueryPlan plan, QueryPlan delegate) {
super(delegate);
this.plan = plan;
this.tableRefs = Sets.newHashSetWithExpectedSize(delegate.getSourceRefs().size() + plan.getSourceRefs().size());
this.tableRefs.addAll(plan.getSourceRefs());
this.tableRefs.addAll(delegate.getSourceRefs());
}
@Override
public Set getSourceRefs() {
return tableRefs;
}
@Override
public ResultIterator iterator(ParallelScanGrouper scanGrouper, Scan scan) throws SQLException {
if (scan == null) {
scan = getDelegate().getContext().getScan();
}
StatementContext context = plan.getContext();
PTable dataTable = plan.getTableRef().getTable();
ResultIterator scanner = null;
try {
if (plan.getStatement().getWhere() != null) {
Filter filter = context.getScan().getFilter();
byte[] filterBytes = filter.toByteArray();
scan.setAttribute(BaseScannerRegionObserver.FILTER_PB_BYTES, filterBytes);
scan.setAttribute(BaseScannerRegionObserver.FILTER_TYPE, Bytes.toBytes(FilterFactory.getFilterType(filter).getNumber()));
}
// Set index maintainer of the index.
List indexes = Lists.newArrayListWithExpectedSize(1);
indexes.add(delegate.getTableRef().getTable());
ImmutableBytesWritable ptr = new ImmutableBytesWritable();
IndexMaintainer.serialize(dataTable, ptr, indexes, context.getConnection());
scan.setAttribute(BaseScannerRegionObserver.INDEX_BUILD_PROTO, ByteUtil.copyKeyBytesIfNecessary(ptr));
if (dataTable.isTransactional()) {
scan.setAttribute(BaseScannerRegionObserver.TX_STATE, context.getConnection().getMutationState().encodeTransaction());
}
scan.setAttribute(SEMI_LEFT_TABLE_NAME,
SchemaUtil.getPhysicalHBaseTableName(dataTable.getSchemaName(), dataTable.getTableName(), dataTable.isNamespaceMapped()).getBytes());
FilterableStatement query = plan.getStatement();
boolean isOrdered = !query.getOrderBy().isEmpty() && !plan.getOrderBy().getOrderByExpressions().isEmpty();
if (!query.isAggregate() && !query.isDistinct() && !isOrdered &&
plan.getLimit() != null && plan.getOffset() != null) {
scan.setAttribute(OFFSET_ON_SERVER,
Bytes.toBytes(QueryUtil.getOffsetLimit(plan.getLimit(), plan.getOffset())));
}
Scan dataScan = context.getScan();
plan.iterator(scanGrouper, dataScan);
for (Map.Entry entry : context.getScan().getAttributesMap().entrySet()){
String attrName = BaseScannerRegionObserver.getSemiAttrName(entry.getKey());
scan.setAttribute(attrName, entry.getValue());
}
scanner = delegate.iterator(scanGrouper, scan);
// add iterators of the parent plans
if (plan.getOffset() != null) {
scanner = new OffsetResultIterator(scanner, plan.getOffset());
}
if (plan.getLimit() != null) {
scanner = new LimitingResultIterator(scanner, plan.getLimit());
}
if (context.getSequenceManager().getSequenceCount() > 0) {
scanner = new SequenceResultIterator(scanner, context.getSequenceManager());
}
} catch (IOException e) {
e.printStackTrace();
}
return scanner;
}
@Override
public ExplainPlan getExplainPlan() throws SQLException {
explainPlanCalled = true;
List planSteps = Lists.newArrayList(delegate.getExplainPlan().getPlanSteps());
boolean exists = planSteps.remove("CLIENT MERGE SORT");
planSteps.add(" SERVER MULTI GET " + plan.getTableRef().getTable().getTableName().getString());
if (exists) {
planSteps.add("CLIENT MERGE SORT");
}
if (plan.getOffset() != null) {
planSteps.add("CLIENT OFFSET " + plan.getOffset());
}
if (plan.getLimit() != null) {
planSteps.add("CLIENT " + plan.getLimit() + " ROW LIMIT");
}
if (delegate.getEstimatedBytesToScan() == null
|| delegate.getEstimatedRowsToScan() == null
|| delegate.getEstimateInfoTimestamp() == null) {
estimatedBytes = null;
estimatedRows = null;
estimateInfoTs = null;
} else {
estimatedBytes = add(plan.getEstimatedBytesToScan(), delegate.getEstimatedBytesToScan());
estimatedRows = add(plan.getEstimatedRowsToScan(), delegate.getEstimatedRowsToScan());
estimateInfoTs = getMin(plan.getEstimateInfoTimestamp(), delegate.getEstimateInfoTimestamp());
}
return new ExplainPlan(planSteps);
}
@Override
public RowProjector getProjector() {
return plan.getProjector();
}
@Override
public FilterableStatement getStatement() {
return plan.getStatement();
}
@Override
public Long getEstimatedRowsToScan() throws SQLException {
if (!explainPlanCalled) {
getExplainPlan();
}
return estimatedRows;
}
@Override
public Long getEstimatedBytesToScan() throws SQLException {
if (!explainPlanCalled) {
getExplainPlan();
}
return estimatedBytes;
}
@Override
public Long getEstimateInfoTimestamp() throws SQLException {
if (!explainPlanCalled) {
getExplainPlan();
}
return estimateInfoTs;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy