Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,7 @@ private boolean keyRequirementSatisfied(Key keyRequirement)
if (maxCardProperty.isAtMostOne()) {
return true;
}
Optional<Key> normalizedKeyRequirement = getNormalizedKey(keyRequirement, equivalenceClassProperty);
if (normalizedKeyRequirement.isPresent()) {
return keyProperty.satisfiesKeyRequirement(keyRequirement);
}
else {
return false;
}
return getNormalizedKey(keyRequirement, equivalenceClassProperty).filter(keyProperty::satisfiesKeyRequirement).isPresent();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.facebook.presto.spi.constraints.PrimaryKeyConstraint;
import com.facebook.presto.spi.constraints.TableConstraint;
import com.facebook.presto.spi.constraints.UniqueConstraint;
import com.facebook.presto.spi.plan.AggregationNode;
import com.facebook.presto.spi.plan.Assignments;
import com.facebook.presto.spi.plan.EquiJoinClause;
import com.facebook.presto.spi.plan.FilterNode;
Expand Down Expand Up @@ -71,6 +72,7 @@
import static com.facebook.presto.sql.relational.Expressions.constant;
import static com.google.common.base.MoreObjects.toStringHelper;
import static java.util.Collections.emptyList;
import static org.testng.Assert.assertTrue;

public class TestLogicalPropertyPropagation
extends BaseRuleTest
Expand Down Expand Up @@ -200,6 +202,58 @@ void testValuesNodeLogicalProperties()
.matches(expectedLogicalProperties);
}

@Test
public void testKeyNormalization()
{
tester().assertThat(new NoOpRule(), logicalPropertiesProvider)
.on(p -> {
TableScanNode customerTableScan = p.tableScan(
customerTableHandle,
ImmutableList.of(customerCustKeyVariable),
ImmutableMap.of(customerCustKeyVariable, customerCustKeyColumn),
TupleDomain.none(),
TupleDomain.none(),
tester().getTableConstraints(customerTableHandle));

TableScanNode ordersTableScan = p.tableScan(
ordersTableHandle,
ImmutableList.of(ordersCustKeyVariable),
ImmutableMap.of(ordersCustKeyVariable, ordersCustKeyColumn),
TupleDomain.none(),
TupleDomain.none(),
tester().getTableConstraints(ordersTableHandle));

TableScanNode lineitemTableScan = p.tableScan(
lineitemTableHandle,
ImmutableList.of(lineitemOrderkeyVariable),
ImmutableMap.of(lineitemOrderkeyVariable, lineitemOrderkeyColumn),
TupleDomain.none(),
TupleDomain.none(),
tester().getTableConstraints(lineitemTableHandle));

JoinNode ordersCustomerJoin = p.join(JoinType.INNER,
ordersTableScan,
customerTableScan,
new EquiJoinClause(ordersCustKeyVariable, customerCustKeyVariable));

AggregationNode aggregation = p.aggregation(builder -> builder
.singleGroupingSet(ordersCustKeyVariable)
.source(p.join(JoinType.INNER,
ordersCustomerJoin,
lineitemTableScan,
new EquiJoinClause(customerCustKeyVariable, lineitemOrderkeyVariable))));
return aggregation;
}).assertLogicalProperties(groupProperties -> {
// SINGLE aggregation on ordersCustKeyVariable => this is a key
assertTrue(groupProperties.isDistinct(ImmutableSet.of(ordersCustKeyVariable)));
// Since ordersCustKeyVariable == customerCustKeyVariable, customerCustKeyVariable is a key as well
// This is derived through the equivalence classes
assertTrue(groupProperties.isDistinct(ImmutableSet.of(customerCustKeyVariable)));
// Same holds true for lineitemOrderkeyVariable
assertTrue(groupProperties.isDistinct(ImmutableSet.of(lineitemOrderkeyVariable)));
});
}

@Test
public void testTableScanNodeLogicalProperties()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

Expand All @@ -58,6 +59,7 @@
import static com.facebook.presto.sql.planner.planPrinter.PlanPrinter.textLogicalPlan;
import static com.facebook.presto.transaction.TransactionBuilder.transaction;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.testng.Assert.fail;

Expand Down Expand Up @@ -131,7 +133,7 @@ public PlanNode get()
TypeProvider types = ruleApplication.types;

if (!ruleApplication.wasRuleApplied()) {
fail(String.format(
fail(format(
"%s did not fire for:\n%s",
rule.getClass().getName(),
formatPlan(plan, types)));
Expand All @@ -145,7 +147,7 @@ public void doesNotFire()
RuleApplication ruleApplication = applyRule();

if (ruleApplication.wasRuleApplied()) {
fail(String.format(
fail(format(
"Expected %s to not fire for:\n%s",
rule.getClass().getName(),
inTransaction(session -> textLogicalPlan(plan, ruleApplication.types, StatsAndCosts.empty(), metadata.getFunctionAndTypeManager(), session, 2))));
Expand All @@ -158,7 +160,7 @@ public void matches(PlanMatchPattern pattern)
TypeProvider types = ruleApplication.types;

if (!ruleApplication.wasRuleApplied()) {
fail(String.format(
fail(format(
"%s did not fire for:\n%s",
rule.getClass().getName(),
formatPlan(plan, types)));
Expand All @@ -167,14 +169,14 @@ public void matches(PlanMatchPattern pattern)
PlanNode actual = ruleApplication.getTransformedPlan();

if (actual == plan) { // plans are not comparable, so we can only ensure they are not the same instance
fail(String.format(
fail(format(
"%s: rule fired but return the original plan:\n%s",
rule.getClass().getName(),
formatPlan(plan, types)));
}

if (!ImmutableSet.copyOf(plan.getOutputVariables()).equals(ImmutableSet.copyOf(actual.getOutputVariables()))) {
fail(String.format(
fail(format(
"%s: output schema of transformed and original plans are not equivalent\n" +
"\texpected: %s\n" +
"\tactual: %s",
Expand All @@ -189,28 +191,35 @@ public void matches(PlanMatchPattern pattern)
});
}

public void matches(LogicalProperties expectedLogicalProperties)
public void assertLogicalProperties(Consumer<LogicalProperties> matcher)
{
RuleApplication ruleApplication = applyRule();
TypeProvider types = ruleApplication.types;
Comment on lines +194 to 197
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Add an explicit assertion when logical properties for the root group are missing to avoid obscure failures in matchers

The helper currently assumes getLogicalProperties(...).get() is always present; if logical properties are missing for the root group, tests will fail with a NoSuchElementException/null rather than a clear assertion. Please assert that the Optional is present (with a descriptive failure message) before invoking matcher.accept(...) so missing logical properties are reported explicitly.

Suggested implementation:

import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

import static org.testng.Assert.assertTrue;
        Optional<LogicalProperties> rootNodeLogicalProperties = ruleApplication.getMemo()
                .getLogicalProperties(ruleApplication.getMemo().getRootGroup());

        assertTrue(
                rootNodeLogicalProperties.isPresent(),
                "Logical properties are missing for the root group; ensure they are computed before asserting.");

        matcher.accept(rootNodeLogicalProperties.get());


if (!ruleApplication.wasRuleApplied()) {
fail(String.format(
fail(format(
"%s did not fire for:\n%s",
rule.getClass().getName(),
formatPlan(plan, types)));
}

// ensure that the logical properties of the root group are equivalent to the expected logical properties
LogicalProperties rootNodeLogicalProperties = ruleApplication.getMemo().getLogicalProperties(ruleApplication.getMemo().getRootGroup()).get();
if (!((LogicalPropertiesImpl) rootNodeLogicalProperties).equals((LogicalPropertiesImpl) expectedLogicalProperties)) {
fail(String.format(
"Logical properties of root node doesn't match expected logical properties\n" +
"\texpected: %s\n" +
"\tactual: %s",
expectedLogicalProperties,
rootNodeLogicalProperties));
}
matcher.accept(rootNodeLogicalProperties);
}

public void matches(LogicalProperties expectedLogicalProperties)
{
// Ensure that the logical properties of the root group are equivalent to the expected logical properties
assertLogicalProperties(rootNodeLogicalProperties -> {
if (!((LogicalPropertiesImpl) rootNodeLogicalProperties).equals((LogicalPropertiesImpl) expectedLogicalProperties)) {
fail(format(
"Logical properties of root node doesn't match expected logical properties\n" +
"\texpected: %s\n" +
"\tactual: %s",
expectedLogicalProperties,
rootNodeLogicalProperties));
}
});
}

private RuleApplication applyRule()
Expand Down
Loading