Skip to content

Instantly share code, notes, and snippets.

@losipiuk
Created June 6, 2016 20:16
Show Gist options
  • Save losipiuk/2e7814e13b1361a825c850d16aa0096d to your computer and use it in GitHub Desktop.
Save losipiuk/2e7814e13b1361a825c850d16aa0096d to your computer and use it in GitHub Desktop.
diff --git a/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java b/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java
index 4f64850..9525c22 100644
--- a/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java
+++ b/presto-main/src/main/java/com/facebook/presto/operator/HashBuilderOperator.java
@@ -17,7 +17,6 @@ import com.facebook.presto.spi.Page;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.sql.planner.Symbol;
import com.facebook.presto.sql.planner.plan.PlanNodeId;
-import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import javax.annotation.concurrent.ThreadSafe;
@@ -68,7 +67,6 @@ public class HashBuilderOperator
requireNonNull(layout, "layout is null"),
outer);
- Preconditions.checkArgument(!hashChannels.isEmpty() || filterFunction.isPresent(), "hashChannels is empty and filterFunction is not set");
this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null"));
this.hashChannel = requireNonNull(hashChannel, "hashChannel is null");
this.filterFunction = requireNonNull(filterFunction, "filterFunction is null");
@@ -138,7 +136,6 @@ public class HashBuilderOperator
this.lookupSourceSupplier = requireNonNull(lookupSourceSupplier, "hashSupplier is null");
- Preconditions.checkArgument(!hashChannels.isEmpty() || filterFunction.isPresent(), "hashChannels is empty and filterFunction is not set");
this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null"));
this.hashChannel = requireNonNull(hashChannel, "hashChannel is null");
this.filterFunction = requireNonNull(filterFunction, "filterFunction is null");
diff --git a/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java b/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java
index 5ada292..4236799 100644
--- a/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java
+++ b/presto-main/src/main/java/com/facebook/presto/operator/PagesIndex.java
@@ -328,8 +328,15 @@ public class PagesIndex
public LookupSource createLookupSource(List<Integer> joinChannels, Optional<Integer> hashChannel, Optional<JoinFilterFunction> filterFunction)
{
- if (!filterFunction.isPresent()) {
- // temporary hack
+ if (!filterFunction.isPresent() && !joinChannels.isEmpty()) {
+ // todo compiled implementation of lookup join does not support:
+ // (1) case with join function and the case
+ // (2) when we are joining with empty join channels.
+
+ // Ad (1) we need to add support for filter function into compiled PagesHashStrategy/JoinProbe
+ // Ad (2) this code path will trigger only for OUTER joins. To fix that we need to add support for
+ // OUTER joins into NestedLoopsJoin and remove "type == INNER" condition in LocalExecutionPlanner.visitJoin()
+
try {
LookupSourceFactory lookupSourceFactory = joinCompiler.compileLookupSourceFactory(types, joinChannels);
diff --git a/presto-main/src/main/java/com/facebook/presto/operator/SimplePagesHashStrategy.java b/presto-main/src/main/java/com/facebook/presto/operator/SimplePagesHashStrategy.java
index c863d59..e207fb7 100644
--- a/presto-main/src/main/java/com/facebook/presto/operator/SimplePagesHashStrategy.java
+++ b/presto-main/src/main/java/com/facebook/presto/operator/SimplePagesHashStrategy.java
@@ -45,7 +45,7 @@ public class SimplePagesHashStrategy
this.filterFunction = filterFunction;
this.types = ImmutableList.copyOf(requireNonNull(types, "types is null"));
this.channels = ImmutableList.copyOf(requireNonNull(channels, "channels is null"));
- channelArrays = buildChannelArrays(channels);
+ this.channelArrays = buildChannelArrays(channels);
checkArgument(types.size() == channels.size(), "Expected types and channels to be the same length");
this.hashChannels = ImmutableList.copyOf(requireNonNull(hashChannels, "hashChannels is null"));
@@ -59,19 +59,18 @@ public class SimplePagesHashStrategy
private List<Block[]> buildChannelArrays(List<List<Block>> channels)
{
- List<Block[]> channelArrays = new ArrayList<>();
if (channels.isEmpty()) {
- channelArrays.add(new Block[] {});
+ return new ArrayList<>();
}
- else {
- int pagesCount = channels.get(0).size();
- for (int i = 0; i < pagesCount; ++i) {
- Block[] blocks = new Block[channels.size()];
- for (int j = 0; j < channels.size(); ++j) {
- blocks[j] = channels.get(j).get(i);
- }
- channelArrays.add(blocks);
+
+ int pagesCount = channels.get(0).size();
+ List<Block[]> channelArrays = new ArrayList<>();
+ for (int i = 0; i < pagesCount; ++i) {
+ Block[] blocks = new Block[channels.size()];
+ for (int j = 0; j < channels.size(); ++j) {
+ blocks[j] = channels.get(j).get(i);
}
+ channelArrays.add(blocks);
}
return channelArrays;
}
@@ -201,6 +200,16 @@ public class SimplePagesHashStrategy
@Override
public boolean applyFilterFunction(int leftBlockIndex, int leftPosition, int rightPosition, Block[] allRightBlocks)
{
- return filterFunction.get().filter(leftPosition, channelArrays.get(leftBlockIndex), rightPosition, allRightBlocks);
+ return filterFunction.get().filter(leftPosition, getLeftBlocks(leftBlockIndex), rightPosition, allRightBlocks);
+ }
+
+ private Block[] getLeftBlocks(int leftBlockIndex)
+ {
+ if (channelArrays.isEmpty()) {
+ return EMPTY_BLOCK_ARRAY;
+ }
+ else {
+ return channelArrays.get(leftBlockIndex);
+ }
}
}
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinProbeCompiler.java b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinProbeCompiler.java
index 3e2be72..3007cd4 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinProbeCompiler.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/gen/JoinProbeCompiler.java
@@ -127,7 +127,9 @@ public class JoinProbeCompiler
DynamicClassLoader classLoader = new DynamicClassLoader(joinProbeClass.getClassLoader());
JoinProbeFactory joinProbeFactory;
- if (filterFunctionPresent) {
+ if (filterFunctionPresent || probeJoinChannel.isEmpty()) {
+ // todo temporary until supported in compiled version
+ // see comment in PagesIndex#createLookupSource
joinProbeFactory = new SimpleJoinProbe.SimpleJoinProbeFactory(types, probeJoinChannel, probeHashChannel);
}
else {
diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java
index 0f74272..7267261 100644
--- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java
+++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/PredicatePushDown.java
@@ -70,7 +70,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
-import static com.facebook.presto.sql.ExpressionUtils.and;
import static com.facebook.presto.sql.ExpressionUtils.combineConjuncts;
import static com.facebook.presto.sql.ExpressionUtils.expressionOrNullSymbols;
import static com.facebook.presto.sql.ExpressionUtils.extractConjuncts;
@@ -227,7 +226,7 @@ public class PredicatePushDown
Map<Boolean, List<Expression>> conjuncts = extractConjuncts(context.get()).stream().collect(Collectors.partitioningBy(pushdownEligiblePredicate));
// Push down conjuncts from the inherited predicate that apply to the common grouping columns, or don't apply to any grouping columns
- PlanNode rewrittenSource = context.rewrite(node.getSource(), combineConjuncts(conjuncts.get(true)));
+ PlanNode rewrittenSource = context.rewrite(node.getSource(), combineConjuncts(conjuncts.get(true)));
PlanNode output = node;
if (rewrittenSource != node.getSource()) {
@@ -357,64 +356,50 @@ public class PredicatePushDown
if (leftSource != node.getLeft() ||
rightSource != node.getRight() ||
!expressionEquivalence.areExpressionsEquivalent(session, newJoinPredicate, joinPredicate, types)) {
- if (node.getType() == JoinNode.Type.INNER && newJoinPredicate.equals(BooleanLiteral.TRUE_LITERAL)) {
- // this rewrite is not valid for OUTER joins as it would give incorrect results in case when we have empty table on INNER side
- // after rewrite query would return no rows, instead rows from OUTER table complemented with NULLs.
- output = new JoinNode(node.getId(), INNER, leftSource, rightSource, ImmutableList.of(), node.getFilter(), Optional.<Symbol>empty(), Optional.<Symbol>empty());
- }
- else {
- // Create identity projections for all existing symbols
- ImmutableMap.Builder<Symbol, Expression> leftProjections = ImmutableMap.builder();
-
- leftProjections.putAll(node.getLeft()
- .getOutputSymbols().stream()
- .collect(Collectors.toMap(key -> key, Symbol::toQualifiedNameReference)));
-
- ImmutableMap.Builder<Symbol, Expression> rightProjections = ImmutableMap.builder();
- rightProjections.putAll(node.getRight()
- .getOutputSymbols().stream()
- .collect(Collectors.toMap(key -> key, Symbol::toQualifiedNameReference)));
-
- // Create new projections for the new join clauses
- ImmutableList.Builder<JoinNode.EquiJoinClause> joinConditionBuilder = ImmutableList.builder();
- ImmutableList.Builder<Expression> joinFilterBuilder = ImmutableList.builder();
- for (Expression conjunct : extractConjuncts(newJoinPredicate)) {
- if (joinEqualityExpression(node.getLeft().getOutputSymbols()).apply(conjunct)) {
- ComparisonExpression equality = (ComparisonExpression) conjunct;
-
- boolean alignedComparison = Iterables.all(DependencyExtractor.extractUnique(equality.getLeft()), in(node.getLeft().getOutputSymbols()));
- Expression leftExpression = (alignedComparison) ? equality.getLeft() : equality.getRight();
- Expression rightExpression = (alignedComparison) ? equality.getRight() : equality.getLeft();
-
- Symbol leftSymbol = symbolAllocator.newSymbol(leftExpression, extractType(leftExpression));
- leftProjections.put(leftSymbol, leftExpression);
- Symbol rightSymbol = symbolAllocator.newSymbol(rightExpression, extractType(rightExpression));
- rightProjections.put(rightSymbol, rightExpression);
-
- joinConditionBuilder.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol));
- }
- else {
- joinFilterBuilder.add(conjunct);
- }
+ // Create identity projections for all existing symbols
+ ImmutableMap.Builder<Symbol, Expression> leftProjections = ImmutableMap.builder();
+
+ leftProjections.putAll(node.getLeft()
+ .getOutputSymbols().stream()
+ .collect(Collectors.toMap(key -> key, Symbol::toQualifiedNameReference)));
+
+ ImmutableMap.Builder<Symbol, Expression> rightProjections = ImmutableMap.builder();
+ rightProjections.putAll(node.getRight()
+ .getOutputSymbols().stream()
+ .collect(Collectors.toMap(key -> key, Symbol::toQualifiedNameReference)));
+
+ // Create new projections for the new join clauses
+ ImmutableList.Builder<JoinNode.EquiJoinClause> joinConditionBuilder = ImmutableList.builder();
+ ImmutableList.Builder<Expression> joinFilterBuilder = ImmutableList.builder();
+ for (Expression conjunct : extractConjuncts(newJoinPredicate)) {
+ if (joinEqualityExpression(node.getLeft().getOutputSymbols()).apply(conjunct)) {
+ ComparisonExpression equality = (ComparisonExpression) conjunct;
+
+ boolean alignedComparison = Iterables.all(DependencyExtractor.extractUnique(equality.getLeft()), in(node.getLeft().getOutputSymbols()));
+ Expression leftExpression = (alignedComparison) ? equality.getLeft() : equality.getRight();
+ Expression rightExpression = (alignedComparison) ? equality.getRight() : equality.getLeft();
+
+ Symbol leftSymbol = symbolAllocator.newSymbol(leftExpression, extractType(leftExpression));
+ leftProjections.put(leftSymbol, leftExpression);
+ Symbol rightSymbol = symbolAllocator.newSymbol(rightExpression, extractType(rightExpression));
+ rightProjections.put(rightSymbol, rightExpression);
+
+ joinConditionBuilder.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol));
}
-
- Optional<Expression> newJoinFilter = Optional.of(combineConjuncts(joinFilterBuilder.build()));
- List<JoinNode.EquiJoinClause> newJoinCondition = joinConditionBuilder.build();
- if (newJoinFilter.get() == BooleanLiteral.TRUE_LITERAL) {
- // we leave TRUE_LITERAL in joinFilter in case of OUTER join when joinConditions are empty.
- // If we change filterFunction to Optional.empty() compiled JoinProbe/PagesHashStrategy would be selected.
- // And those do not support case with 0 hash channels yet.
- // We are good for INNER join as this will be executed as NestedLoopJoin.
- if (!newJoinCondition.isEmpty() || node.getType() == INNER) {
- newJoinFilter = Optional.empty();
- }
+ else {
+ joinFilterBuilder.add(conjunct);
}
+ }
- leftSource = new ProjectNode(idAllocator.getNextId(), leftSource, leftProjections.build());
- rightSource = new ProjectNode(idAllocator.getNextId(), rightSource, rightProjections.build());
-
- output = new JoinNode(node.getId(), node.getType(), leftSource, rightSource, newJoinCondition, newJoinFilter, node.getLeftHashSymbol(), node.getRightHashSymbol());
+ Optional<Expression> newJoinFilter = Optional.of(combineConjuncts(joinFilterBuilder.build()));
+ if (newJoinFilter.get() == BooleanLiteral.TRUE_LITERAL) {
+ newJoinFilter = Optional.empty();
}
+
+ leftSource = new ProjectNode(idAllocator.getNextId(), leftSource, leftProjections.build());
+ rightSource = new ProjectNode(idAllocator.getNextId(), rightSource, rightProjections.build());
+
+ output = new JoinNode(node.getId(), node.getType(), leftSource, rightSource, joinConditionBuilder.build(), newJoinFilter, node.getLeftHashSymbol(), node.getRightHashSymbol());
}
if (!postJoinPredicate.equals(BooleanLiteral.TRUE_LITERAL)) {
output = new FilterNode(idAllocator.getNextId(), output, postJoinPredicate);
@@ -468,7 +453,7 @@ public class PredicatePushDown
}
// See if we can push down any outer predicates to the inner side
- for (Expression conjunct : EqualityInference.nonInferrableConjuncts(and(outerEffectivePredicate))) {
+ for (Expression conjunct : EqualityInference.nonInferrableConjuncts(outerEffectivePredicate)) {
Expression rewritten = potentialNullSymbolInference.rewriteExpression(conjunct, not(in(outerSymbols)));
if (rewritten != null) {
innerPushdownConjuncts.add(rewritten);
@@ -664,7 +649,7 @@ public class PredicatePushDown
for (JoinNode.EquiJoinClause equiJoinClause : joinNode.getCriteria()) {
builder.add(equalsExpression(equiJoinClause.getLeft(), equiJoinClause.getRight()));
}
- joinNode.getFilter().ifPresent(filter -> builder.add(filter));
+ joinNode.getFilter().ifPresent(builder::add);
return combineConjuncts(builder.build());
}
diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
index c720e39..53e1f54 100644
--- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
+++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestQueries.java
@@ -1978,6 +1978,21 @@ public abstract class AbstractTestQueries
"SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) LEFT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND d > 0",
"VALUES (1, 1, 1, 1), (1, 1, 1, 2), (1, 2, 1, 1), (1, 2, 1, 2)");
assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) LEFT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND c = d",
+ "VALUES (1, 1, 1, 1), (1, 2, 1, 1)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) LEFT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND c < d",
+ "VALUES (1, 1, 1, 2), (1, 2, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) LEFT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON c = d",
+ "VALUES (1, 1, 1, 1), (1, 2, 1, 1)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) LEFT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON c < d",
+ "VALUES (1, 1, 1, 2), (1, 2, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES 1, 2) t1(a) LEFT OUTER JOIN (VALUES 10, 11) t2(b) ON 1 = 1",
+ "VALUES (1, 10), (1, 11), (2, 10), (2, 11)");
+ assertQuery(
"SELECT * FROM (VALUES 1, 2) t1(a) LEFT OUTER JOIN (VALUES 10, 11) t2(b) ON a > 1",
"VALUES (1, NULL), (2, 11), (2, 10)");
assertQuery(
@@ -2053,6 +2068,21 @@ public abstract class AbstractTestQueries
"SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) RIGHT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND d > 0",
"VALUES (1, 1, 1, 1), (1, 1, 1, 2), (1, 2, 1, 1), (1, 2, 1, 2)");
assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) RIGHT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND c = d",
+ "VALUES (1, 2, 1, 1), (1, 1, 1, 1), (NULL, NULL, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) RIGHT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON a=c AND c < d",
+ "VALUES (NULL, NULL, 1, 1), (1, 2, 1, 2), (1, 1, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) RIGHT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON c = d",
+ "VALUES (1, 1, 1, 1), (1, 2, 1, 1), (NULL, NULL, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES (1,1), (1,2)) t1(a,b) RIGHT OUTER JOIN (VALUES (1,1), (1,2)) t2(c,d) ON c < d",
+ "VALUES (NULL, NULL, 1, 1), (1, 1, 1, 2), (1, 2, 1, 2)");
+ assertQuery(
+ "SELECT * FROM (VALUES 1, 2) t1(a) RIGHT OUTER JOIN (VALUES 10, 11) t2(b) ON 1 = 1",
+ "VALUES (1, 10), (1, 11), (2, 10), (2, 11)");
+ assertQuery(
"SELECT * FROM (VALUES 1, 2) t1(a) RIGHT OUTER JOIN (VALUES 10, 11) t2(b) ON a > 1",
"VALUES (2, 11), (2, 10)");
assertQuery(
@@ -2067,6 +2097,73 @@ public abstract class AbstractTestQueries
}
@Test
+ public void testJoinUsingSymbolsFromJustOneSideOfJoin() throws Exception
+ {
+ assertQuery(
+ "SELECT b FROM (VALUES 1, 2) t1(a) RIGHT OUTER JOIN (VALUES 10, 11) t2(b) ON b > 10",
+ "VALUES (10), (11), (11)");
+ assertQuery(
+ "SELECT a FROM (VALUES 1, 2) t1(a) RIGHT OUTER JOIN (VALUES 10, 11) t2(b) ON a > 1",
+ "VALUES (2), (2)");
+ assertQuery(
+ "SELECT b FROM (VALUES 1, 2) t1(a) LEFT OUTER JOIN (VALUES 10, 11) t2(b) ON b > 10",
+ "VALUES (11), (11)");
+ assertQuery(
+ "SELECT a FROM (VALUES 1, 2) t1(a) LEFT OUTER JOIN (VALUES 10, 11) t2(b) ON a > 1",
+ "VALUES (1), (2), (2)");
+ assertQuery(
+ "SELECT a FROM (VALUES 1, 2) t1(a) JOIN (VALUES 10, 11) t2(b) ON a > 1",
+ "VALUES (2), (2)");
+ assertQuery(
+ "SELECT b FROM (VALUES 1, 2) t1(a) JOIN (VALUES 10, 11) t2(b) ON b > 10",
+ "VALUES (11), (11)");
+ }
+
+ @Test
+ public void testJoinsWithTrueJoinCondition() throws Exception
+ {
+ // inner join
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (0, 10), (0, 11), (1, 10), (1, 11)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+
+ // left join
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) LEFT JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (0, 10), (0, 11), (1, 10), (1, 11)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) LEFT JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) LEFT JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "VALUES (0, NULL), (1, NULL)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) LEFT JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+
+ // right join
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) RIGHT JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (0, 10), (0, 11), (1, 10), (1, 11)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) RIGHT JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (NULL, 10), (NULL, 11)");
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) RIGHT JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) RIGHT JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+
+ // full join
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) FULL JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (0, 10), (0, 11), (1, 10), (1, 11)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) FULL JOIN (VALUES 10, 11) t2(b) ON TRUE",
+ "VALUES (NULL, 10), (NULL, 11)");
+ assertQuery("SELECT * FROM (VALUES 0, 1) t1(a) FULL JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "VALUES (0, NULL), (1, NULL)");
+ assertQuery("SELECT * FROM (SELECT 1 WHERE FALSE) t1(a) FULL JOIN (SELECT 1 WHERE FALSE) t2(b) ON TRUE",
+ "SELECT 1 WHERE FALSE");
+ }
+
+ @Test
public void testNonEqualityFullJoin()
throws Exception
{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment