use of io.trino.spi.connector.JoinApplicationResult in project trino by trinodb.
the class PushJoinIntoTableScan method apply.
@Override
public Result apply(JoinNode joinNode, Captures captures, Context context) {
if (joinNode.isCrossJoin()) {
return Result.empty();
}
TableScanNode left = captures.get(LEFT_TABLE_SCAN);
TableScanNode right = captures.get(RIGHT_TABLE_SCAN);
verify(!left.isUpdateTarget() && !right.isUpdateTarget(), "Unexpected Join over for-update table scan");
Expression effectiveFilter = getEffectiveFilter(joinNode);
FilterSplitResult filterSplitResult = splitFilter(effectiveFilter, left.getOutputSymbols(), right.getOutputSymbols(), context);
if (!filterSplitResult.getRemainingFilter().equals(BooleanLiteral.TRUE_LITERAL)) {
// TODO add extra filter node above join
return Result.empty();
}
if (left.getEnforcedConstraint().isNone() || right.getEnforcedConstraint().isNone()) {
// enforced constraint harder below.
return Result.empty();
}
Map<String, ColumnHandle> leftAssignments = left.getAssignments().entrySet().stream().collect(toImmutableMap(entry -> entry.getKey().getName(), Map.Entry::getValue));
Map<String, ColumnHandle> rightAssignments = right.getAssignments().entrySet().stream().collect(toImmutableMap(entry -> entry.getKey().getName(), Map.Entry::getValue));
/*
* We are (lazily) computing estimated statistics for join node and left and right table
* and passing those to connector via applyJoin.
*
* There are a couple reasons for this approach:
* - the engine knows how to estimate join and connector may not
* - the engine may have cached stats for the table scans (within context.getStatsProvider()), so can be able to provide information more inexpensively
* - in the future, the engine may be able to provide stats for table scan even in case when connector no longer can (see https://github.com/trinodb/trino/issues/6998)
* - the pushdown feasibility assessment logic may be different (or configured differently) for different connectors/catalogs.
*/
JoinStatistics joinStatistics = getJoinStatistics(joinNode, left, right, context);
Optional<JoinApplicationResult<TableHandle>> joinApplicationResult = metadata.applyJoin(context.getSession(), getJoinType(joinNode), left.getTable(), right.getTable(), filterSplitResult.getPushableConditions(), // TODO we could pass only subset of assignments here, those which are needed to resolve filterSplitResult.getPushableConditions
leftAssignments, rightAssignments, joinStatistics);
if (joinApplicationResult.isEmpty()) {
return Result.empty();
}
TableHandle handle = joinApplicationResult.get().getTableHandle();
Map<ColumnHandle, ColumnHandle> leftColumnHandlesMapping = joinApplicationResult.get().getLeftColumnHandles();
Map<ColumnHandle, ColumnHandle> rightColumnHandlesMapping = joinApplicationResult.get().getRightColumnHandles();
ImmutableMap.Builder<Symbol, ColumnHandle> assignmentsBuilder = ImmutableMap.builder();
assignmentsBuilder.putAll(left.getAssignments().entrySet().stream().collect(toImmutableMap(Map.Entry::getKey, entry -> leftColumnHandlesMapping.get(entry.getValue()))));
assignmentsBuilder.putAll(right.getAssignments().entrySet().stream().collect(toImmutableMap(Map.Entry::getKey, entry -> rightColumnHandlesMapping.get(entry.getValue()))));
Map<Symbol, ColumnHandle> assignments = assignmentsBuilder.buildOrThrow();
// convert enforced constraint
JoinNode.Type joinType = joinNode.getType();
TupleDomain<ColumnHandle> leftConstraint = deriveConstraint(left.getEnforcedConstraint(), leftColumnHandlesMapping, joinType == RIGHT || joinType == FULL);
TupleDomain<ColumnHandle> rightConstraint = deriveConstraint(right.getEnforcedConstraint(), rightColumnHandlesMapping, joinType == LEFT || joinType == FULL);
TupleDomain<ColumnHandle> newEnforcedConstraint = TupleDomain.withColumnDomains(ImmutableMap.<ColumnHandle, Domain>builder().putAll(leftConstraint.getDomains().orElseThrow()).putAll(rightConstraint.getDomains().orElseThrow()).buildOrThrow());
return Result.ofPlanNode(new ProjectNode(context.getIdAllocator().getNextId(), new TableScanNode(joinNode.getId(), handle, ImmutableList.copyOf(assignments.keySet()), assignments, newEnforcedConstraint, deriveTableStatisticsForPushdown(context.getStatsProvider(), context.getSession(), joinApplicationResult.get().isPrecalculateStatistics(), joinNode), false, Optional.empty()), Assignments.identity(joinNode.getOutputSymbols())));
}
use of io.trino.spi.connector.JoinApplicationResult in project trino by trinodb.
the class TestPushJoinIntoTableScan method testPushJoinIntoTableRequiresFullColumnHandleMappingInResult.
@Test
public void testPushJoinIntoTableRequiresFullColumnHandleMappingInResult() {
try (RuleTester ruleTester = defaultRuleTester()) {
MockConnectorFactory connectorFactory = createMockConnectorFactory((session, applyJoinType, left, right, joinConditions, leftAssignments, rightAssignments) -> Optional.of(new JoinApplicationResult<>(JOIN_CONNECTOR_TABLE_HANDLE, ImmutableMap.of(COLUMN_A1_HANDLE, JOIN_COLUMN_A1_HANDLE, COLUMN_A2_HANDLE, JOIN_COLUMN_A2_HANDLE), // mapping for COLUMN_B1_HANDLE is missing
ImmutableMap.of(), false)));
ruleTester.getQueryRunner().createCatalog(MOCK_CATALOG, connectorFactory, ImmutableMap.of());
assertThatThrownBy(() -> {
ruleTester.assertThat(new PushJoinIntoTableScan(ruleTester.getMetadata())).on(p -> {
Symbol columnA1Symbol = p.symbol(COLUMN_A1);
Symbol columnA2Symbol = p.symbol(COLUMN_A2);
Symbol columnB1Symbol = p.symbol(COLUMN_B1);
TupleDomain<ColumnHandle> leftContraint = TupleDomain.fromFixedValues(ImmutableMap.of(COLUMN_A2_HANDLE, NullableValue.of(BIGINT, 44L)));
TupleDomain<ColumnHandle> rightConstraint = TupleDomain.fromFixedValues(ImmutableMap.of(COLUMN_B1_HANDLE, NullableValue.of(BIGINT, 45L)));
TableScanNode left = p.tableScan(TABLE_A_HANDLE, ImmutableList.of(columnA1Symbol, columnA2Symbol), ImmutableMap.of(columnA1Symbol, COLUMN_A1_HANDLE, columnA2Symbol, COLUMN_A2_HANDLE), leftContraint);
TableScanNode right = p.tableScan(TABLE_B_HANDLE, ImmutableList.of(columnB1Symbol), ImmutableMap.of(columnB1Symbol, COLUMN_B1_HANDLE), rightConstraint);
return p.join(INNER, left, right, new JoinNode.EquiJoinClause(columnA1Symbol, columnB1Symbol));
}).withSession(MOCK_SESSION).matches(anyTree());
}).isInstanceOf(IllegalStateException.class).hasMessageContaining("Column handle mappings do not match old column handles");
}
}
use of io.trino.spi.connector.JoinApplicationResult in project trino by trinodb.
the class TestPushJoinIntoTableScan method testPushJoinIntoTableScanPreservesEnforcedConstraint.
@Test(dataProvider = "testPushJoinIntoTableScanPreservesEnforcedConstraintParams")
public void testPushJoinIntoTableScanPreservesEnforcedConstraint(JoinNode.Type joinType, TupleDomain<ColumnHandle> leftConstraint, TupleDomain<ColumnHandle> rightConstraint, TupleDomain<Predicate<ColumnHandle>> expectedConstraint) {
try (RuleTester ruleTester = defaultRuleTester()) {
MockConnectorFactory connectorFactory = createMockConnectorFactory((session, applyJoinType, left, right, joinConditions, leftAssignments, rightAssignments) -> Optional.of(new JoinApplicationResult<>(JOIN_CONNECTOR_TABLE_HANDLE, JOIN_TABLE_A_COLUMN_MAPPING, JOIN_TABLE_B_COLUMN_MAPPING, false)));
ruleTester.getQueryRunner().createCatalog(MOCK_CATALOG, connectorFactory, ImmutableMap.of());
ruleTester.assertThat(new PushJoinIntoTableScan(ruleTester.getMetadata())).on(p -> {
Symbol columnA1Symbol = p.symbol(COLUMN_A1);
Symbol columnA2Symbol = p.symbol(COLUMN_A2);
Symbol columnB1Symbol = p.symbol(COLUMN_B1);
TableScanNode left = p.tableScan(TABLE_A_HANDLE, ImmutableList.of(columnA1Symbol, columnA2Symbol), ImmutableMap.of(columnA1Symbol, COLUMN_A1_HANDLE, columnA2Symbol, COLUMN_A2_HANDLE), leftConstraint);
TableScanNode right = p.tableScan(TABLE_B_HANDLE, ImmutableList.of(columnB1Symbol), ImmutableMap.of(columnB1Symbol, COLUMN_B1_HANDLE), rightConstraint);
return p.join(joinType, left, right, new JoinNode.EquiJoinClause(columnA1Symbol, columnB1Symbol));
}).withSession(MOCK_SESSION).matches(project(tableScan(tableHandle -> JOIN_PUSHDOWN_SCHEMA_TABLE_NAME.equals(((MockConnectorTableHandle) tableHandle).getTableName()), expectedConstraint, ImmutableMap.of())));
}
}
use of io.trino.spi.connector.JoinApplicationResult in project trino by trinodb.
the class MetadataManager method applyJoin.
@Override
public Optional<JoinApplicationResult<TableHandle>> applyJoin(Session session, JoinType joinType, TableHandle left, TableHandle right, List<JoinCondition> joinConditions, Map<String, ColumnHandle> leftAssignments, Map<String, ColumnHandle> rightAssignments, JoinStatistics statistics) {
if (!right.getCatalogName().equals(left.getCatalogName())) {
// Exact comparison is fine as catalog name here is passed from CatalogMetadata and is normalized to lowercase
return Optional.empty();
}
CatalogName catalogName = left.getCatalogName();
ConnectorTransactionHandle transaction = left.getTransaction();
ConnectorMetadata metadata = getMetadata(session, catalogName);
ConnectorSession connectorSession = session.toConnectorSession(catalogName);
Optional<JoinApplicationResult<ConnectorTableHandle>> connectorResult = metadata.applyJoin(connectorSession, joinType, left.getConnectorHandle(), right.getConnectorHandle(), joinConditions, leftAssignments, rightAssignments, statistics);
return connectorResult.map(result -> {
Set<ColumnHandle> leftColumnHandles = ImmutableSet.copyOf(getColumnHandles(session, left).values());
Set<ColumnHandle> rightColumnHandles = ImmutableSet.copyOf(getColumnHandles(session, right).values());
Set<ColumnHandle> leftColumnHandlesMappingKeys = result.getLeftColumnHandles().keySet();
Set<ColumnHandle> rightColumnHandlesMappingKeys = result.getRightColumnHandles().keySet();
if (leftColumnHandlesMappingKeys.size() != leftColumnHandles.size() || rightColumnHandlesMappingKeys.size() != rightColumnHandles.size() || !leftColumnHandlesMappingKeys.containsAll(leftColumnHandles) || !rightColumnHandlesMappingKeys.containsAll(rightColumnHandles)) {
throw new IllegalStateException(format("Column handle mappings do not match old column handles: left=%s; right=%s; newLeft=%s, newRight=%s", leftColumnHandles, rightColumnHandles, leftColumnHandlesMappingKeys, rightColumnHandlesMappingKeys));
}
return new JoinApplicationResult<>(new TableHandle(catalogName, result.getTableHandle(), transaction), result.getLeftColumnHandles(), result.getRightColumnHandles(), result.isPrecalculateStatistics());
});
}
Aggregations