Created
June 6, 2016 20:16
-
-
Save losipiuk/2e7814e13b1361a825c850d16aa0096d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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