All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.table.operations.utils.OperationExpressionsUtils Maven / Gradle / Ivy

Go to download

There is a newer version: 2.0-preview1
Show newest version
/*
 * 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.flink.table.operations.utils;

import org.apache.flink.annotation.Internal;
import org.apache.flink.table.expressions.CallExpression;
import org.apache.flink.table.expressions.Expression;
import org.apache.flink.table.expressions.FieldReferenceExpression;
import org.apache.flink.table.expressions.LocalReferenceExpression;
import org.apache.flink.table.expressions.LookupCallExpression;
import org.apache.flink.table.expressions.ResolvedExpression;
import org.apache.flink.table.expressions.TableReferenceExpression;
import org.apache.flink.table.expressions.UnresolvedCallExpression;
import org.apache.flink.table.expressions.utils.ApiExpressionDefaultVisitor;
import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
import org.apache.flink.table.functions.FunctionDefinition;
import org.apache.flink.table.operations.QueryOperation;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.apache.flink.table.expressions.ApiExpressionUtils.isFunctionOfKind;
import static org.apache.flink.table.expressions.ApiExpressionUtils.unresolvedCall;
import static org.apache.flink.table.expressions.ApiExpressionUtils.unresolvedRef;
import static org.apache.flink.table.expressions.ApiExpressionUtils.valueLiteral;
import static org.apache.flink.table.expressions.ExpressionUtils.extractValue;
import static org.apache.flink.table.functions.BuiltInFunctionDefinitions.AS;
import static org.apache.flink.table.functions.BuiltInFunctionDefinitions.WINDOW_PROPERTIES;
import static org.apache.flink.table.functions.FunctionKind.AGGREGATE;

/**
 * Utility methods for transforming {@link Expression} to use them in {@link QueryOperation}s.
 *
 * 

Note: Some of these utilities are intended to be used before expressions are fully resolved * and some afterwards. */ @Internal public class OperationExpressionsUtils { // -------------------------------------------------------------------------------------------- // Pre-expression resolution utils // -------------------------------------------------------------------------------------------- /** Container for extracted expressions of the same family. */ public static class CategorizedExpressions { private final List projections; private final List aggregations; private final List windowProperties; CategorizedExpressions( List projections, List aggregations, List windowProperties) { this.projections = projections; this.aggregations = aggregations; this.windowProperties = windowProperties; } public List getProjections() { return projections; } public List getAggregations() { return aggregations; } public List getWindowProperties() { return windowProperties; } } /** * Extracts and deduplicates all aggregation and window property expressions (zero, one, or * more) from the given expressions. * * @param expressions a list of expressions to extract * @return a Tuple2, the first field contains the extracted and deduplicated aggregations, and * the second field contains the extracted and deduplicated window properties. */ public static CategorizedExpressions extractAggregationsAndProperties( List expressions) { AggregationAndPropertiesSplitter splitter = new AggregationAndPropertiesSplitter(); expressions.forEach(expr -> expr.accept(splitter)); List projections = expressions.stream() .map( expr -> expr.accept( new AggregationAndPropertiesReplacer( splitter.aggregates, splitter.properties))) .collect(Collectors.toList()); List aggregates = nameExpressions(splitter.aggregates); List properties = nameExpressions(splitter.properties); return new CategorizedExpressions(projections, aggregates, properties); } private static List nameExpressions(Map expressions) { return expressions.entrySet().stream() .map(entry -> unresolvedCall(AS, entry.getKey(), valueLiteral(entry.getValue()))) .collect(Collectors.toList()); } private static class AggregationAndPropertiesSplitter extends ApiExpressionDefaultVisitor { private int uniqueId = 0; private final Map aggregates = new LinkedHashMap<>(); private final Map properties = new LinkedHashMap<>(); @Override public Void visit(LookupCallExpression unresolvedCall) { throw new IllegalStateException( "All lookup calls should be resolved by now. Got: " + unresolvedCall); } @Override public Void visit(UnresolvedCallExpression unresolvedCall) { FunctionDefinition functionDefinition = unresolvedCall.getFunctionDefinition(); if (isFunctionOfKind(unresolvedCall, AGGREGATE)) { aggregates.computeIfAbsent(unresolvedCall, expr -> "EXPR$" + uniqueId++); } else if (WINDOW_PROPERTIES.contains(functionDefinition)) { properties.computeIfAbsent(unresolvedCall, expr -> "EXPR$" + uniqueId++); } else { unresolvedCall.getChildren().forEach(c -> c.accept(this)); } return null; } @Override protected Void defaultMethod(Expression expression) { return null; } } private static class AggregationAndPropertiesReplacer extends ApiExpressionDefaultVisitor { private final Map aggregates; private final Map properties; private AggregationAndPropertiesReplacer( Map aggregates, Map properties) { this.aggregates = aggregates; this.properties = properties; } @Override public Expression visit(LookupCallExpression unresolvedCall) { throw new IllegalStateException( "All lookup calls should be resolved by now. Got: " + unresolvedCall); } @Override public Expression visit(CallExpression call) { throw new IllegalStateException("All calls should still be unresolved by now."); } @Override public Expression visit(UnresolvedCallExpression unresolvedCall) { if (aggregates.get(unresolvedCall) != null) { return unresolvedRef(aggregates.get(unresolvedCall)); } else if (properties.get(unresolvedCall) != null) { return unresolvedRef(properties.get(unresolvedCall)); } final List args = unresolvedCall.getChildren().stream() .map(c -> c.accept(this)) .collect(Collectors.toList()); return unresolvedCall.replaceArgs(args); } @Override protected Expression defaultMethod(Expression expression) { return expression; } } // -------------------------------------------------------------------------------------------- // utils that can be used both before and after resolution // -------------------------------------------------------------------------------------------- private static final ExtractNameVisitor extractNameVisitor = new ExtractNameVisitor(); /** * Extracts names from given expressions if they have one. Expressions that have names are: * *

    *
  • {@link FieldReferenceExpression} *
  • {@link TableReferenceExpression} *
  • {@link LocalReferenceExpression} *
  • {@link BuiltInFunctionDefinitions#AS} *
* * @param expressions list of expressions to extract names from * @return corresponding list of optional names */ public static List> extractNames(List expressions) { return expressions.stream() .map(OperationExpressionsUtils::extractName) .collect(Collectors.toList()); } /** * Extracts name from given expression if it has one. Expressions that have names are: * *
    *
  • {@link FieldReferenceExpression} *
  • {@link TableReferenceExpression} *
  • {@link LocalReferenceExpression} *
  • {@link BuiltInFunctionDefinitions#AS} *
* * @param expression expression to extract name from * @return optional name of given expression */ public static Optional extractName(Expression expression) { return expression.accept(extractNameVisitor); } private static class ExtractNameVisitor extends ApiExpressionDefaultVisitor> { @Override public Optional visit(LookupCallExpression lookupCall) { throw new IllegalStateException("All lookup calls should be resolved by now."); } @Override public Optional visit(UnresolvedCallExpression unresolvedCall) { if (unresolvedCall.getFunctionDefinition() == AS) { return extractValue(unresolvedCall.getChildren().get(1), String.class); } else { return Optional.empty(); } } @Override public Optional visit(CallExpression call) { if (call.getFunctionDefinition() == AS) { return extractValue(call.getChildren().get(1), String.class); } else { return Optional.empty(); } } @Override public Optional visit(LocalReferenceExpression localReference) { return Optional.of(localReference.getName()); } @Override public Optional visit(TableReferenceExpression tableReference) { return Optional.of(tableReference.getName()); } @Override public Optional visit(FieldReferenceExpression fieldReference) { return Optional.of(fieldReference.getName()); } @Override protected Optional defaultMethod(Expression expression) { return Optional.empty(); } } private OperationExpressionsUtils() {} }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy