Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed 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 io.trino.sql.gen;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
import io.airlift.bytecode.BytecodeBlock;
import io.airlift.bytecode.BytecodeNode;
import io.airlift.bytecode.Scope;
import io.airlift.bytecode.Variable;
import io.airlift.bytecode.control.IfStatement;
import io.airlift.bytecode.control.SwitchStatement.SwitchBuilder;
import io.airlift.bytecode.instruction.LabelNode;
import io.trino.metadata.ResolvedFunction;
import io.trino.spi.type.Type;
import io.trino.sql.relational.ConstantExpression;
import io.trino.sql.relational.RowExpression;
import io.trino.sql.relational.SpecialForm;
import io.trino.util.FastutilSetHelper;
import java.lang.invoke.MethodHandle;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Throwables.throwIfUnchecked;
import static io.airlift.bytecode.expression.BytecodeExpressions.constantFalse;
import static io.airlift.bytecode.expression.BytecodeExpressions.constantTrue;
import static io.airlift.bytecode.expression.BytecodeExpressions.invokeStatic;
import static io.airlift.bytecode.instruction.JumpInstruction.jump;
import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL;
import static io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL;
import static io.trino.spi.function.InvocationConvention.InvocationReturnConvention.NULLABLE_RETURN;
import static io.trino.spi.function.InvocationConvention.simpleConvention;
import static io.trino.spi.function.OperatorType.EQUAL;
import static io.trino.spi.function.OperatorType.HASH_CODE;
import static io.trino.spi.function.OperatorType.INDETERMINATE;
import static io.trino.sql.gen.BytecodeUtils.ifWasNullPopAndGoto;
import static io.trino.sql.gen.BytecodeUtils.invoke;
import static io.trino.sql.gen.BytecodeUtils.loadConstant;
import static io.trino.util.FastutilSetHelper.toFastutilHashSet;
import static java.lang.Math.toIntExact;
public class InCodeGenerator
implements BytecodeGenerator
{
private final RowExpression valueExpression;
private final List testExpressions;
private final ResolvedFunction resolvedEqualsFunction;
private final ResolvedFunction resolvedHashCodeFunction;
private final ResolvedFunction resolvedIsIndeterminate;
public InCodeGenerator(SpecialForm specialForm)
{
checkArgument(specialForm.arguments().size() >= 2, "At least two arguments are required");
valueExpression = specialForm.arguments().get(0);
testExpressions = specialForm.arguments().subList(1, specialForm.arguments().size());
checkArgument(specialForm.functionDependencies().size() == 3);
resolvedEqualsFunction = specialForm.getOperatorDependency(EQUAL);
resolvedHashCodeFunction = specialForm.getOperatorDependency(HASH_CODE);
resolvedIsIndeterminate = specialForm.getOperatorDependency(INDETERMINATE);
}
enum SwitchGenerationCase
{
DIRECT_SWITCH,
HASH_SWITCH,
SET_CONTAINS
}
@VisibleForTesting
static SwitchGenerationCase checkSwitchGenerationCase(Type type, List values)
{
if (values.size() >= 8) {
// SET_CONTAINS is generally faster for not super tiny IN lists.
// Tipping point is between 5 and 10 (using round 8)
return SwitchGenerationCase.SET_CONTAINS;
}
if (type.getJavaType() != long.class) {
return SwitchGenerationCase.HASH_SWITCH;
}
for (RowExpression expression : values) {
// For non-constant expressions, they will be added to the default case in the generated switch code. They do not affect any of
// the cases other than the default one. Therefore, it's okay to skip them when choosing between DIRECT_SWITCH and HASH_SWITCH.
// Same argument applies for nulls.
if (!(expression instanceof ConstantExpression constantExpression)) {
continue;
}
Object constant = constantExpression.value();
if (constant == null) {
continue;
}
long longConstant = ((Number) constant).longValue();
if (longConstant < Integer.MIN_VALUE || longConstant > Integer.MAX_VALUE) {
return SwitchGenerationCase.HASH_SWITCH;
}
}
return SwitchGenerationCase.DIRECT_SWITCH;
}
@Override
public BytecodeNode generateExpression(BytecodeGeneratorContext generatorContext)
{
Type type = valueExpression.type();
Class> javaType = type.getJavaType();
SwitchGenerationCase switchGenerationCase = checkSwitchGenerationCase(type, testExpressions);
MethodHandle equalsMethodHandle = generatorContext.getScalarFunctionImplementation(resolvedEqualsFunction, simpleConvention(NULLABLE_RETURN, NEVER_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle hashCodeMethodHandle = generatorContext.getScalarFunctionImplementation(resolvedHashCodeFunction, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle indeterminateMethodHandle = generatorContext.getScalarFunctionImplementation(resolvedIsIndeterminate, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
ImmutableListMultimap.Builder hashBucketsBuilder = ImmutableListMultimap.builder();
ImmutableList.Builder defaultBucket = ImmutableList.builder();
ImmutableSet.Builder