/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.processor.relational; import java.util.Collections; import org.teiid.common.buffer.BlockedException; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.util.Assertion; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Criteria; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.lang.QueryCommand; /** * Takes a query with 1 or more dependent sets from 1 or more sources and creates a series of commands. Dependent sets from the * same source are treated as a special case. If there are multiple batches from that source, we will create replacement criteria * in lock step - rather than forming the full cartesian product space. This implementation assumes that ordering will be * respected above the access node and that the incoming dependent tuple values are already sorted. */ public class DependentAccessNode extends AccessNode { //plan state private int maxSetSize; private int maxPredicates; private boolean pushdown; //processing state private DependentCriteriaProcessor criteriaProcessor; private Criteria dependentCrit; private boolean sort = true; /** * Cached rewritten command to be used as the base for all dependent queries. */ private Command rewrittenCommand; private boolean useBindings; private boolean complexQuery; public DependentAccessNode(int nodeID) { super(nodeID); } /** * @see org.teiid.query.processor.relational.AccessNode#close() */ public void closeDirect() { super.closeDirect(); if (criteriaProcessor != null) { criteriaProcessor.close(); } } public void reset() { super.reset(); criteriaProcessor = null; dependentCrit = null; sort = true; rewrittenCommand = null; } @Override protected Command nextCommand() throws TeiidProcessingException, TeiidComponentException { if (rewrittenCommand == null) { Command atomicCommand = super.nextCommand(); try { rewriteAndEvaluate(atomicCommand, getEvaluator(Collections.emptyMap()), this.getContext(), this.getContext().getMetadata()); } catch (BlockedException e) { //we must decline already as the parent will assume open, and it will //be too late if (sort && ((Query)atomicCommand).getOrderBy() != null) { declineSort(); } throw e; } rewrittenCommand = atomicCommand; nextCommand = null; } if (nextCommand == null && rewrittenCommand != null) { nextCommand = (Command)rewrittenCommand.clone(); } return super.nextCommand(); } public Object clone() { DependentAccessNode clonedNode = new DependentAccessNode(super.getID()); clonedNode.maxSetSize = this.maxSetSize; clonedNode.maxPredicates = this.maxPredicates; clonedNode.pushdown = this.pushdown; clonedNode.useBindings = this.useBindings; clonedNode.complexQuery = this.complexQuery; super.copyTo(clonedNode); return clonedNode; } /** * @return Returns the maxSize. */ public int getMaxSetSize() { return this.maxSetSize; } public int getMaxPredicates() { return maxPredicates; } public void setMaxPredicates(int maxPredicates) { this.maxPredicates = maxPredicates; } /** * @param maxSize * The maxSize to set. */ public void setMaxSetSize(int maxSize) { this.maxSetSize = maxSize; } /** * @see org.teiid.query.processor.relational.AccessNode#prepareNextCommand(org.teiid.query.sql.lang.Command) */ protected boolean prepareNextCommand(Command atomicCommand) throws TeiidComponentException, TeiidProcessingException { Assertion.assertTrue(atomicCommand instanceof Query); Query query = (Query)atomicCommand; if (this.criteriaProcessor == null) { this.criteriaProcessor = new DependentCriteriaProcessor(this.maxSetSize, this.maxPredicates, this, query.getCriteria()); this.criteriaProcessor.setPushdown(pushdown); this.criteriaProcessor.setUseBindings(useBindings); this.criteriaProcessor.setComplexQuery(complexQuery); } if (this.dependentCrit == null) { dependentCrit = criteriaProcessor.prepareCriteria(); } query.setCriteria(dependentCrit); //walk up the tree and notify the parent join it is responsible for the sort if (sort && query.getOrderBy() != null && criteriaProcessor.hasNextCommand()) { declineSort(); } if (!sort) { query.setOrderBy(null); } boolean result = RelationalNodeUtil.shouldExecute(atomicCommand, true); dependentCrit = null; criteriaProcessor.consumedCriteria(); return result; } private void declineSort() { RelationalNode parent = this.getParent(); RelationalNode child = this; while (parent != null && !(parent instanceof JoinNode)) { child = parent; parent = parent.getParent(); } if (parent != null) { JoinNode joinNode = (JoinNode)parent; if (joinNode.getJoinStrategy() instanceof MergeJoinStrategy) { MergeJoinStrategy mjs = (MergeJoinStrategy)joinNode.getJoinStrategy(); if (joinNode.getChildren()[0] == child) { mjs.setProcessingSortLeft(true); } else { mjs.setProcessingSortRight(true); } } } sort = false; } /** * @see org.teiid.query.processor.relational.AccessNode#hasNextCommand() */ protected boolean hasNextCommand() { return criteriaProcessor.hasNextCommand(); } public void setPushdown(boolean pushdown) { this.pushdown = pushdown; } @Override public Boolean requiresTransaction(boolean transactionalReads) { Boolean required = super.requiresTransaction(transactionalReads); if (required != null) { return required; } if (transactionalReads || !(this.getCommand() instanceof QueryCommand)) { return true; } return null; } public boolean isUseBindings() { return useBindings; } public void setUseBindings(boolean useBindings) { this.useBindings = useBindings; } public void setComplexQuery(boolean complexQuery) { this.complexQuery = complexQuery; } }