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

com.hazelcast.org.apache.calcite.rex.RexNormalize Maven / Gradle / Ivy

There is a newer version: 5.5.0
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 com.hazelcast.org.apache.calcite.rex;

import com.hazelcast.org.apache.calcite.config.CalciteSystemProperty;
import com.hazelcast.org.apache.calcite.sql.SqlKind;
import com.hazelcast.org.apache.calcite.sql.SqlOperator;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.util.Pair;

import com.hazelcast.com.google.common.collect.ImmutableList;

import org.apiguardian.api.API;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
 * Context required to normalize a row-expression.
 *
 * 

Currently, only simple normalization is supported, such as: * *

    *
  • $2 = $1 → $1 = $2
  • *
  • $2 > $1 → $1 < $2
  • *
  • 1.23 = $1 → $1 = 1.23
  • *
  • OR(OR(udf($1), $2), $3) → OR($3, OR($2, udf($1)))
  • *
* *

In the future, this component may extend to support more normalization cases * for general promotion. e.g. the strategy to decide which operand is more complex * should be more smart. * *

There is no one normalization strategy that works for all cases, and no consensus about what * the desired strategies should be. So by default, the normalization is disabled. We do force * normalization when computing the digest of {@link RexCall}s during planner planning. */ public class RexNormalize { private RexNormalize() {} /** * Normalizes the variables of a rex call. * * @param operator The operator * @param operands The operands * * @return normalized variables of the call or the original * if there is no need to normalize */ @API(since = "1.24", status = API.Status.EXPERIMENTAL) public static Pair> normalize( SqlOperator operator, List operands) { final Pair> original = Pair.of(operator, operands); if (!allowsNormalize() || operands.size() != 2) { return original; } final RexNode operand0 = operands.get(0); final RexNode operand1 = operands.get(1); // If arguments are the same, then we normalize < vs > // '<' == 60, '>' == 62, so we prefer <. final SqlKind kind = operator.getKind(); final SqlKind reversedKind = kind.reverse(); final int x = reversedKind.compareTo(kind); if (x < 0) { return Pair.of(requireNonNull(operator.reverse()), ImmutableList.of(operand1, operand0)); } if (x > 0) { return original; } if (!isSymmetricalCall(operator, operand0, operand1)) { return original; } if (reorderOperands(operand0, operand1) < 0) { // $0=$1 is the same as $1=$0, so we make sure the digest is the same for them. // When $1 > $0 is normalized, the operation needs to be flipped // so we sort arguments first, then flip the sign. return Pair.of(requireNonNull(operator.reverse()), ImmutableList.of(operand1, operand0)); } return original; } /** * Computes the hashCode of a rex call. We ignore the operands sequence when the call is * symmetrical. * *

Note that the logic to decide whether operands need reordering * should be strictly same with {@link #normalize}. */ public static int hashCode( SqlOperator operator, List operands) { if (!allowsNormalize() || operands.size() != 2) { return Objects.hash(operator, operands); } // If arguments are the same, then we normalize < vs > // '<' == 60, '>' == 62, so we prefer <. final SqlKind kind = operator.getKind(); final SqlKind reversedKind = kind.reverse(); final int x = reversedKind.compareTo(kind); if (x < 0) { return Objects.hash(requireNonNull(operator.reverse()), Arrays.asList(operands.get(1), operands.get(0))); } if (isSymmetricalCall(operator, operands.get(0), operands.get(1))) { return Objects.hash(operator, unorderedHash(operands)); } return Objects.hash(operator, operands); } /** * Compares two operands to see which one should be normalized to be in front of the other. * *

We can always use the #hashCode to reorder the operands, do it as a fallback to keep * good readability. * * @param operand0 First operand * @param operand1 Second operand * * @return non-negative (>=0) if {@code operand0} should be in the front, * negative if {@code operand1} should be in the front */ private static int reorderOperands(RexNode operand0, RexNode operand1) { // Reorder the operands based on the SqlKind enumeration sequence, // smaller is in the behind, e.g. the literal is behind of input ref and AND, OR. int x = operand0.getKind().compareTo(operand1.getKind()); // If the operands are same kind, use the hashcode to reorder. // Note: the RexInputRef's hash code is its index. return x != 0 ? x : operand1.hashCode() - operand0.hashCode(); } /** Returns whether a call is symmetrical. **/ private static boolean isSymmetricalCall( SqlOperator operator, RexNode operand0, RexNode operand1) { return operator.isSymmetrical() || SqlKind.SYMMETRICAL_SAME_ARG_TYPE.contains(operator.getKind()) && SqlTypeUtil.equalSansNullability(operand0.getType(), operand1.getType()); } /** Compute a hash that is symmetric in its arguments - that is a hash * where the order of appearance of elements does not matter. * This is useful for hashing symmetrical rex calls, for example. */ private static int unorderedHash(List xs) { int a = 0; int b = 0; int c = 1; for (Object x : xs) { int h = Objects.hashCode(x); a += h; b ^= h; if (h != 0) { c *= h; } } return (a * 17 + b) * 17 + c; } /** * The digest of {@code RexNode} is normalized by default. * * @return true if the digest allows normalization */ private static boolean allowsNormalize() { return CalciteSystemProperty.ENABLE_REX_DIGEST_NORMALIZE.value(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy