/** * 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.hadoop.hive.ql.parse; import java.util.ArrayList; import java.util.List; import org.apache.hadoop.hive.ql.exec.PTFUtils; public class PTFInvocationSpec { PartitionedTableFunctionSpec function; public PartitionedTableFunctionSpec getFunction() { return function; } public void setFunction(PartitionedTableFunctionSpec function) { this.function = function; } public PartitionedTableFunctionSpec getStartOfChain() { return function == null ? null : function.getStartOfChain(); } public String getQueryInputName() { return function == null ? null : function.getQueryInputName(); } public PTFQueryInputSpec getQueryInput() { return function == null ? null : function.getQueryInput(); } /* * A PTF Input represents the input to a PTF Function. An Input can be a Hive SubQuery or Table * or another PTF Function. An Input instance captures the ASTNode that this instance was created from. */ public abstract static class PTFInputSpec { ASTNode astNode; public ASTNode getAstNode() { return astNode; } public void setAstNode(ASTNode astNode) { this.astNode = astNode; } public abstract PTFInputSpec getInput(); public abstract String getQueryInputName(); public abstract PTFQueryInputSpec getQueryInput(); } public static enum PTFQueryInputType { TABLE, SUBQUERY, PTFCOMPONENT, WINDOWING; } /* * A PTF input that represents a source in the overall Query. This could be a Table or a SubQuery. * If a PTF chain requires execution by multiple PTF Operators; * then the original Invocation object is decomposed into a set of Component Invocations. * Every component Invocation but the first one ends in a PTFQueryInputSpec instance. * During the construction of the Operator plan a PTFQueryInputSpec object in the chain implies connect the PTF Operator to the * 'input' i.e. has been generated so far. */ public static class PTFQueryInputSpec extends PTFInputSpec { String source; PTFQueryInputType type; public String getSource() { return source; } public void setSource(String source) { this.source = source; } public PTFQueryInputType getType() { return type; } public void setType(PTFQueryInputType type) { this.type = type; } @Override public PTFInputSpec getInput() { return null; } @Override public String getQueryInputName() { return getSource(); } @Override public PTFQueryInputSpec getQueryInput() { return this; } } /* * Represents a PTF Invocation. Captures: * - function name and alias * - the Partitioning details about its input * - its arguments. The ASTNodes representing the arguments are captured here. * - a reference to its Input */ public static class PartitionedTableFunctionSpec extends PTFInputSpec { String name; String alias; List<ASTNode> args; PartitioningSpec partitioning; PTFInputSpec input; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAlias() { return alias; } public void setAlias(String alias) { this.alias = alias; } public List<ASTNode> getArgs() { return args; } public void setArgs(List<ASTNode> args) { this.args = args; } public PartitioningSpec getPartitioning() { return partitioning; } public void setPartitioning(PartitioningSpec partitioning) { this.partitioning = partitioning; } @Override public PTFInputSpec getInput() { return input; } public void setInput(PTFInputSpec input) { this.input = input; } public PartitionSpec getPartition() { return getPartitioning() == null ? null : getPartitioning().getPartSpec(); } public void setPartition(PartitionSpec partSpec) { partitioning = partitioning == null ? new PartitioningSpec() : partitioning; partitioning.setPartSpec(partSpec); } public OrderSpec getOrder() { return getPartitioning() == null ? null : getPartitioning().getOrderSpec(); } public void setOrder(OrderSpec orderSpec) { partitioning = partitioning == null ? new PartitioningSpec() : partitioning; partitioning.setOrderSpec(orderSpec); } public void addArg(ASTNode arg) { args = args == null ? new ArrayList<ASTNode>() : args; args.add(arg); } public PartitionedTableFunctionSpec getStartOfChain() { if ( input instanceof PartitionedTableFunctionSpec ) { return ((PartitionedTableFunctionSpec)input).getStartOfChain(); } return this; } @Override public String getQueryInputName() { return input.getQueryInputName(); } @Override public PTFQueryInputSpec getQueryInput() { return input.getQueryInput(); } } /* * Captures how the Input to a PTF Function should be partitioned and * ordered. Refers to a /Partition/ and /Order/ instance. */ public static class PartitioningSpec { PartitionSpec partSpec; OrderSpec orderSpec; public PartitionSpec getPartSpec() { return partSpec; } public void setPartSpec(PartitionSpec partSpec) { this.partSpec = partSpec; } public OrderSpec getOrderSpec() { return orderSpec; } public void setOrderSpec(OrderSpec orderSpec) { this.orderSpec = orderSpec; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((orderSpec == null) ? 0 : orderSpec.hashCode()); result = prime * result + ((partSpec == null) ? 0 : partSpec.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } PartitioningSpec other = (PartitioningSpec) obj; if (orderSpec == null) { if (other.orderSpec != null) { return false; } } else if (!orderSpec.equals(other.orderSpec)) { return false; } if (partSpec == null) { if (other.partSpec != null) { return false; } } else if (!partSpec.equals(other.partSpec)) { return false; } return true; } @Override public String toString() { return String.format("PartitioningSpec=[%s%s]", partSpec == null ? "" : partSpec, orderSpec == null ? "" : orderSpec); } } /* * Captures how an Input should be Partitioned. This is captured as a * list of ASTNodes that are the expressions in the Distribute/Cluster * by clause specifying the partitioning applied for a PTF invocation. */ public static class PartitionSpec { ArrayList<PartitionExpression> expressions; public ArrayList<PartitionExpression> getExpressions() { return expressions; } public void setExpressions(ArrayList<PartitionExpression> columns) { this.expressions = columns; } public void addExpression(PartitionExpression c) { expressions = expressions == null ? new ArrayList<PartitionExpression>() : expressions; expressions.add(c); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((expressions == null) ? 0 : expressions.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } PartitionSpec other = (PartitionSpec) obj; if (expressions == null) { if (other.expressions != null) { return false; } } else if (!expressions.equals(other.expressions)) { return false; } return true; } @Override public String toString() { return String.format("partitionColumns=%s",PTFUtils.toString(expressions)); } } public static class PartitionExpression { ASTNode expression; public PartitionExpression() {} public PartitionExpression(PartitionExpression peSpec) { expression = peSpec.getExpression(); } public ASTNode getExpression() { return expression; } public void setExpression(ASTNode expression) { this.expression = expression; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((expression == null) ? 0 : expression.toStringTree().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!getClass().isAssignableFrom(obj.getClass())) { return false; } PartitionExpression other = (PartitionExpression) obj; if (expression == null) { if (other.expression != null) { return false; } } else if (!expression.toStringTree().equals(other.expression.toStringTree())) { return false; } return true; } @Override public String toString() { return expression.toStringTree(); } } /* * Captures how the Input should be Ordered. This is captured as a list * of ASTNodes that are the expressions in the Sort By clause in a * PTF invocation. */ public static class OrderSpec { ArrayList<OrderExpression> expressions; public OrderSpec() {} public OrderSpec(PartitionSpec pSpec) { for(PartitionExpression peSpec : pSpec.getExpressions()) { addExpression(new OrderExpression(peSpec)); } } public ArrayList<OrderExpression> getExpressions() { return expressions; } public void setExpressions(ArrayList<OrderExpression> columns) { this.expressions = columns; } /** * Add order expressions from the list of expressions in the format of ASTNode * @param args */ public void addExpressions(ArrayList<ASTNode> nodes) { for (int i = 0; i < nodes.size(); i++) { OrderExpression expr = new OrderExpression(); expr.setExpression(nodes.get(i)); addExpression(expr); } } public void addExpression(OrderExpression c) { expressions = expressions == null ? new ArrayList<OrderExpression>() : expressions; expressions.add(c); } protected boolean isPrefixedBy(PartitionSpec pSpec) { if ( pSpec == null || pSpec.getExpressions() == null) { return true; } int pExprCnt = pSpec.getExpressions().size(); int exprCnt = getExpressions() == null ? 0 : getExpressions().size(); if ( exprCnt < pExprCnt ) { return false; } for(int i=0; i < pExprCnt; i++) { if ( !pSpec.getExpressions().get(i).equals(getExpressions().get(i)) ) { return false; } } return true; } protected void prefixBy(PartitionSpec pSpec) { if ( pSpec == null || pSpec.getExpressions() == null) { return; } if ( expressions == null ) { expressions = new ArrayList<PTFInvocationSpec.OrderExpression>(); } for(int i = pSpec.getExpressions().size() - 1; i >= 0; i--) { expressions.add(0, new OrderExpression(pSpec.getExpressions().get(i))); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((expressions == null) ? 0 : expressions.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } OrderSpec other = (OrderSpec) obj; if (expressions == null) { if (other.expressions != null) { return false; } } else if (!expressions.equals(other.expressions)) { return false; } return true; } @Override public String toString() { return String.format("orderColumns=%s",PTFUtils.toString(expressions)); } } public static enum Order { ASC, DESC; } public static enum NullOrder { NULLS_FIRST, NULLS_LAST; } public static class OrderExpression extends PartitionExpression { Order order; NullOrder nullOrder; public OrderExpression() { order = Order.ASC; nullOrder = NullOrder.NULLS_FIRST; } public OrderExpression(PartitionExpression peSpec) { super(peSpec); order = Order.ASC; nullOrder = NullOrder.NULLS_FIRST; } public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } public NullOrder getNullOrder() { return nullOrder; } public void setNullOrder(NullOrder nullOrder) { this.nullOrder = nullOrder; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((order == null) ? 0 : order.hashCode()); result = prime * result + ((nullOrder == null) ? 0 : nullOrder.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } OrderExpression other = (OrderExpression) obj; if (order != other.order) { return false; } if (nullOrder != other.nullOrder) { return false; } return true; } @Override public String toString() { return String.format("%s %s %s", super.toString(), order, nullOrder); } } }