Search in sources :

Example 1 with RowExpression

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;
}
Also used : LabelNode(io.airlift.bytecode.instruction.LabelNode) Variable(io.airlift.bytecode.Variable) ImmutableList(com.google.common.collect.ImmutableList) ConstantExpression(io.trino.sql.relational.ConstantExpression) IfStatement(io.airlift.bytecode.control.IfStatement) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableListMultimap(com.google.common.collect.ImmutableListMultimap) BytecodeNode(io.airlift.bytecode.BytecodeNode) BytecodeBlock(io.airlift.bytecode.BytecodeBlock) RowExpression(io.trino.sql.relational.RowExpression) Type(io.trino.spi.type.Type) Scope(io.airlift.bytecode.Scope) SwitchBuilder(io.airlift.bytecode.control.SwitchStatement.SwitchBuilder) Collection(java.util.Collection) Map(java.util.Map) MethodHandle(java.lang.invoke.MethodHandle)

Example 2 with RowExpression

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);
}
Also used : LabelNode(io.airlift.bytecode.instruction.LabelNode) IfStatement(io.airlift.bytecode.control.IfStatement) Variable(io.airlift.bytecode.Variable) Scope(io.airlift.bytecode.Scope) BytecodeBlock(io.airlift.bytecode.BytecodeBlock) RowExpression(io.trino.sql.relational.RowExpression) BytecodeNode(io.airlift.bytecode.BytecodeNode) SpecialForm(io.trino.sql.relational.SpecialForm)

Example 3 with RowExpression

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);
}
Also used : CursorProcessor(io.trino.operator.project.CursorProcessor) RowExpression(io.trino.sql.relational.RowExpression) Page(io.trino.spi.Page) FixedPageSource(io.trino.spi.connector.FixedPageSource) PlanNodeId(io.trino.sql.planner.plan.PlanNodeId) PageProcessor(io.trino.operator.project.PageProcessor) CatalogName(io.trino.connector.CatalogName) Split(io.trino.metadata.Split) TestingSplit(io.trino.testing.TestingSplit) MaterializedResult(io.trino.testing.MaterializedResult) OperatorAssertion.toMaterializedResult(io.trino.operator.OperatorAssertion.toMaterializedResult) Test(org.testng.annotations.Test)

Example 4 with RowExpression

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));
}
Also used : CursorProcessor(io.trino.operator.project.CursorProcessor) RowExpression(io.trino.sql.relational.RowExpression) Page(io.trino.spi.Page) FixedPageSource(io.trino.spi.connector.FixedPageSource) PlanNodeId(io.trino.sql.planner.plan.PlanNodeId) PageProcessor(io.trino.operator.project.PageProcessor) CatalogName(io.trino.connector.CatalogName) Split(io.trino.metadata.Split) TestingSplit(io.trino.testing.TestingSplit) Test(org.testng.annotations.Test)

Example 5 with RowExpression

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);
}
Also used : CursorProcessor(io.trino.operator.project.CursorProcessor) RowExpression(io.trino.sql.relational.RowExpression) Page(io.trino.spi.Page) PageRecordSet(io.trino.operator.index.PageRecordSet) RecordPageSource(io.trino.spi.connector.RecordPageSource) PlanNodeId(io.trino.sql.planner.plan.PlanNodeId) PageProcessor(io.trino.operator.project.PageProcessor) CatalogName(io.trino.connector.CatalogName) Split(io.trino.metadata.Split) TestingSplit(io.trino.testing.TestingSplit) MaterializedResult(io.trino.testing.MaterializedResult) OperatorAssertion.toMaterializedResult(io.trino.operator.OperatorAssertion.toMaterializedResult) Test(org.testng.annotations.Test)

Aggregations

RowExpression (io.trino.sql.relational.RowExpression)34 Test (org.testng.annotations.Test)14 PageProcessor (io.trino.operator.project.PageProcessor)13 Page (io.trino.spi.Page)9 PlanNodeId (io.trino.sql.planner.plan.PlanNodeId)9 CursorProcessor (io.trino.operator.project.CursorProcessor)7 CallExpression (io.trino.sql.relational.CallExpression)7 MaterializedResult (io.trino.testing.MaterializedResult)7 ImmutableList (com.google.common.collect.ImmutableList)6 BytecodeBlock (io.airlift.bytecode.BytecodeBlock)6 Variable (io.airlift.bytecode.Variable)6 CatalogName (io.trino.connector.CatalogName)6 Split (io.trino.metadata.Split)6 ExpressionCompiler (io.trino.sql.gen.ExpressionCompiler)6 TestingSplit (io.trino.testing.TestingSplit)6 IfStatement (io.airlift.bytecode.control.IfStatement)5 Type (io.trino.spi.type.Type)5 Expression (io.trino.sql.tree.Expression)5 LabelNode (io.airlift.bytecode.instruction.LabelNode)4 FunctionManager (io.trino.metadata.FunctionManager)4