use of io.trino.sql.relational.RowExpression in project trino by trinodb.
the class InCodeGenerator method generateExpression.
@Override
public BytecodeNode generateExpression(BytecodeGeneratorContext generatorContext) {
Type type = valueExpression.getType();
Class<?> javaType = type.getJavaType();
SwitchGenerationCase switchGenerationCase = checkSwitchGenerationCase(type, testExpressions);
MethodHandle equalsMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedEqualsFunction, simpleConvention(NULLABLE_RETURN, NEVER_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle hashCodeMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedHashCodeFunction, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
MethodHandle indeterminateMethodHandle = generatorContext.getScalarFunctionInvoker(resolvedIsIndeterminate, simpleConvention(FAIL_ON_NULL, NEVER_NULL)).getMethodHandle();
ImmutableListMultimap.Builder<Integer, BytecodeNode> hashBucketsBuilder = ImmutableListMultimap.builder();
ImmutableList.Builder<BytecodeNode> defaultBucket = ImmutableList.builder();
ImmutableSet.Builder<Object> constantValuesBuilder = ImmutableSet.builder();
for (RowExpression testValue : testExpressions) {
BytecodeNode testBytecode = generatorContext.generate(testValue);
if (isDeterminateConstant(testValue, indeterminateMethodHandle)) {
ConstantExpression constant = (ConstantExpression) testValue;
Object object = constant.getValue();
switch(switchGenerationCase) {
case DIRECT_SWITCH:
case SET_CONTAINS:
constantValuesBuilder.add(object);
break;
case HASH_SWITCH:
try {
int hashCode = Long.hashCode((Long) hashCodeMethodHandle.invoke(object));
hashBucketsBuilder.put(hashCode, testBytecode);
} catch (Throwable throwable) {
throw new IllegalArgumentException("Error processing IN statement: error calculating hash code for " + object, throwable);
}
break;
default:
throw new IllegalArgumentException("Not supported switch generation case: " + switchGenerationCase);
}
} else {
defaultBucket.add(testBytecode);
}
}
ImmutableListMultimap<Integer, BytecodeNode> hashBuckets = hashBucketsBuilder.build();
ImmutableSet<Object> constantValues = constantValuesBuilder.build();
LabelNode end = new LabelNode("end");
LabelNode match = new LabelNode("match");
LabelNode noMatch = new LabelNode("noMatch");
LabelNode defaultLabel = new LabelNode("default");
Scope scope = generatorContext.getScope();
Variable value = scope.createTempVariable(javaType);
BytecodeNode switchBlock;
Variable expression = scope.createTempVariable(int.class);
SwitchBuilder switchBuilder = new SwitchBuilder().expression(expression);
switch(switchGenerationCase) {
case DIRECT_SWITCH:
// For these types, it's safe to not use Trino HASH_CODE and EQUAL operator.
for (Object constantValue : constantValues) {
switchBuilder.addCase(toIntExact((Long) constantValue), jump(match));
}
switchBuilder.defaultCase(jump(defaultLabel));
switchBlock = new BytecodeBlock().comment("lookupSwitch(<stackValue>))").append(new IfStatement().condition(invokeStatic(InCodeGenerator.class, "isInteger", boolean.class, value)).ifFalse(new BytecodeBlock().gotoLabel(defaultLabel))).append(expression.set(value.cast(int.class))).append(switchBuilder.build());
break;
case HASH_SWITCH:
for (Map.Entry<Integer, Collection<BytecodeNode>> bucket : hashBuckets.asMap().entrySet()) {
Collection<BytecodeNode> testValues = bucket.getValue();
BytecodeBlock caseBlock = buildInCase(generatorContext, scope, resolvedEqualsFunction, match, defaultLabel, value, testValues, false, resolvedIsIndeterminate);
switchBuilder.addCase(bucket.getKey(), caseBlock);
}
switchBuilder.defaultCase(jump(defaultLabel));
Binding hashCodeBinding = generatorContext.getCallSiteBinder().bind(hashCodeMethodHandle);
switchBlock = new BytecodeBlock().comment("lookupSwitch(hashCode(<stackValue>))").getVariable(value).append(invoke(hashCodeBinding, resolvedHashCodeFunction.getSignature())).invokeStatic(Long.class, "hashCode", int.class, long.class).putVariable(expression).append(switchBuilder.build());
break;
case SET_CONTAINS:
Set<?> constantValuesSet = toFastutilHashSet(constantValues, type, hashCodeMethodHandle, equalsMethodHandle);
Binding constant = generatorContext.getCallSiteBinder().bind(constantValuesSet, constantValuesSet.getClass());
switchBlock = new BytecodeBlock().comment("inListSet.contains(<stackValue>)").append(new IfStatement().condition(new BytecodeBlock().comment("value").getVariable(value).comment("set").append(loadConstant(constant)).invokeStatic(FastutilSetHelper.class, "in", boolean.class, javaType.isPrimitive() ? javaType : Object.class, constantValuesSet.getClass())).ifTrue(jump(match)));
break;
default:
throw new IllegalArgumentException("Not supported switch generation case: " + switchGenerationCase);
}
BytecodeBlock defaultCaseBlock = buildInCase(generatorContext, scope, resolvedEqualsFunction, match, noMatch, value, defaultBucket.build(), true, resolvedIsIndeterminate).setDescription("default");
BytecodeBlock block = new BytecodeBlock().comment("IN").append(generatorContext.generate(valueExpression)).append(ifWasNullPopAndGoto(scope, end, boolean.class, javaType)).putVariable(value).append(switchBlock).visitLabel(defaultLabel).append(defaultCaseBlock);
BytecodeBlock matchBlock = new BytecodeBlock().setDescription("match").visitLabel(match).append(generatorContext.wasNull().set(constantFalse())).push(true).gotoLabel(end);
block.append(matchBlock);
BytecodeBlock noMatchBlock = new BytecodeBlock().setDescription("noMatch").visitLabel(noMatch).push(false).gotoLabel(end);
block.append(noMatchBlock);
block.visitLabel(end);
return block;
}
use of io.trino.sql.relational.RowExpression in project trino by trinodb.
the class SwitchCodeGenerator method generateExpression.
@Override
public BytecodeNode generateExpression(BytecodeGeneratorContext generatorContext) {
// TODO: compile as
/*
hashCode = hashCode(<value>)
// all constant expressions before a non-constant
switch (hashCode) {
case ...:
if (<value> == <constant1>) {
...
}
else if (<value> == <constant2>) {
...
}
else if (...) {
}
case ...:
...
}
if (<value> == <non-constant1>) {
...
}
else if (<value> == <non-constant2>) {
...
}
...
// repeat with next sequence of constant expressions
*/
Scope scope = generatorContext.getScope();
// process value, else, and all when clauses
BytecodeNode valueBytecode = generatorContext.generate(value);
BytecodeNode elseValue;
if (this.elseValue.isEmpty()) {
elseValue = new BytecodeBlock().append(generatorContext.wasNull().set(constantTrue())).pushJavaDefault(returnType.getJavaType());
} else {
elseValue = generatorContext.generate(this.elseValue.get());
}
// determine the type of the value and result
Class<?> valueType = value.getType().getJavaType();
// evaluate the value and store it in a variable
LabelNode nullValue = new LabelNode("nullCondition");
Variable tempVariable = scope.createTempVariable(valueType);
BytecodeBlock block = new BytecodeBlock().append(valueBytecode).append(BytecodeUtils.ifWasNullClearPopAndGoto(scope, nullValue, void.class, valueType)).putVariable(tempVariable);
BytecodeNode getTempVariableNode = VariableInstruction.loadVariable(tempVariable);
// build the statements
elseValue = new BytecodeBlock().visitLabel(nullValue).append(elseValue);
// reverse list because current if statement builder doesn't support if/else so we need to build the if statements bottom up
for (int i = whenClauses.size() - 1; i >= 0; i--) {
SpecialForm clause = whenClauses.get(i);
RowExpression operand = clause.getArguments().get(0);
RowExpression result = clause.getArguments().get(1);
// call equals(value, operand)
// TODO: what if operand is null? It seems that the call will return "null" (which is cleared below)
// and the code only does the right thing because the value in the stack for that scenario is
// Java's default for boolean == false
// This code should probably be checking for wasNull after the call and "failing" the equality
// check if wasNull is true
BytecodeNode equalsCall = generatorContext.generateCall(equalsFunctions.get(i), ImmutableList.of(generatorContext.generate(operand), getTempVariableNode));
BytecodeBlock condition = new BytecodeBlock().append(equalsCall).append(generatorContext.wasNull().set(constantFalse()));
elseValue = new IfStatement("when").condition(condition).ifTrue(generatorContext.generate(result)).ifFalse(elseValue);
}
return block.append(elseValue);
}
use of io.trino.sql.relational.RowExpression in project trino by trinodb.
the class TestScanFilterAndProjectOperator method testPageSource.
@Test
public void testPageSource() {
Page input = SequencePageBuilder.createSequencePage(ImmutableList.of(VARCHAR), 10_000, 0);
DriverContext driverContext = newDriverContext();
List<RowExpression> projections = ImmutableList.of(field(0, VARCHAR));
Supplier<CursorProcessor> cursorProcessor = functionAssertions.getExpressionCompiler().compileCursorProcessor(Optional.empty(), projections, "key");
Supplier<PageProcessor> pageProcessor = functionAssertions.getExpressionCompiler().compilePageProcessor(Optional.empty(), projections);
ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory factory = new ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory(0, new PlanNodeId("test"), new PlanNodeId("0"), (session, split, table, columns, dynamicFilter) -> new FixedPageSource(ImmutableList.of(input)), cursorProcessor, pageProcessor, TEST_TABLE_HANDLE, ImmutableList.of(), DynamicFilter.EMPTY, ImmutableList.of(VARCHAR), DataSize.ofBytes(0), 0);
SourceOperator operator = factory.createOperator(driverContext);
operator.addSplit(new Split(new CatalogName("test"), TestingSplit.createLocalSplit(), Lifespan.taskWide()));
operator.noMoreSplits();
MaterializedResult expected = toMaterializedResult(driverContext.getSession(), ImmutableList.of(VARCHAR), ImmutableList.of(input));
MaterializedResult actual = toMaterializedResult(driverContext.getSession(), ImmutableList.of(VARCHAR), toPages(operator));
assertEquals(actual.getRowCount(), expected.getRowCount());
assertEquals(actual, expected);
}
use of io.trino.sql.relational.RowExpression in project trino by trinodb.
the class TestScanFilterAndProjectOperator method testPageSourceMergeOutput.
@Test
public void testPageSourceMergeOutput() {
List<Page> input = rowPagesBuilder(BIGINT).addSequencePage(100, 0).addSequencePage(100, 0).addSequencePage(100, 0).addSequencePage(100, 0).build();
RowExpression filter = call(functionAssertions.getTestingFunctionResolution().resolveOperator(EQUAL, ImmutableList.of(BIGINT, BIGINT)), field(0, BIGINT), constant(10L, BIGINT));
List<RowExpression> projections = ImmutableList.of(field(0, BIGINT));
Supplier<CursorProcessor> cursorProcessor = functionAssertions.getExpressionCompiler().compileCursorProcessor(Optional.of(filter), projections, "key");
Supplier<PageProcessor> pageProcessor = functionAssertions.getExpressionCompiler().compilePageProcessor(Optional.of(filter), projections);
ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory factory = new ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory(0, new PlanNodeId("test"), new PlanNodeId("0"), (session, split, table, columns, dynamicFilter) -> new FixedPageSource(input), cursorProcessor, pageProcessor, TEST_TABLE_HANDLE, ImmutableList.of(), DynamicFilter.EMPTY, ImmutableList.of(BIGINT), DataSize.of(64, KILOBYTE), 2);
SourceOperator operator = factory.createOperator(newDriverContext());
operator.addSplit(new Split(new CatalogName("test"), TestingSplit.createLocalSplit(), Lifespan.taskWide()));
operator.noMoreSplits();
List<Page> actual = toPages(operator);
assertEquals(actual.size(), 1);
List<Page> expected = rowPagesBuilder(BIGINT).row(10L).row(10L).row(10L).row(10L).build();
assertPageEquals(ImmutableList.of(BIGINT), actual.get(0), expected.get(0));
}
use of io.trino.sql.relational.RowExpression in project trino by trinodb.
the class TestScanFilterAndProjectOperator method testRecordCursorSource.
@Test
public void testRecordCursorSource() {
Page input = SequencePageBuilder.createSequencePage(ImmutableList.of(VARCHAR), 10_000, 0);
DriverContext driverContext = newDriverContext();
List<RowExpression> projections = ImmutableList.of(field(0, VARCHAR));
Supplier<CursorProcessor> cursorProcessor = functionAssertions.getExpressionCompiler().compileCursorProcessor(Optional.empty(), projections, "key");
Supplier<PageProcessor> pageProcessor = functionAssertions.getExpressionCompiler().compilePageProcessor(Optional.empty(), projections);
ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory factory = new ScanFilterAndProjectOperator.ScanFilterAndProjectOperatorFactory(0, new PlanNodeId("test"), new PlanNodeId("0"), (session, split, table, columns, dynamicFilter) -> new RecordPageSource(new PageRecordSet(ImmutableList.of(VARCHAR), input)), cursorProcessor, pageProcessor, TEST_TABLE_HANDLE, ImmutableList.of(), DynamicFilter.EMPTY, ImmutableList.of(VARCHAR), DataSize.ofBytes(0), 0);
SourceOperator operator = factory.createOperator(driverContext);
operator.addSplit(new Split(new CatalogName("test"), TestingSplit.createLocalSplit(), Lifespan.taskWide()));
operator.noMoreSplits();
MaterializedResult expected = toMaterializedResult(driverContext.getSession(), ImmutableList.of(VARCHAR), ImmutableList.of(input));
MaterializedResult actual = toMaterializedResult(driverContext.getSession(), ImmutableList.of(VARCHAR), toPages(operator));
assertEquals(actual.getRowCount(), expected.getRowCount());
assertEquals(actual, expected);
}
Aggregations