[ASTERIXDB-3605][COMP] Fix Required Partitioning properties while doing left outer join

 - user model changes: no
 - storage format changes: no
 - interface changes: no

Ext-ref: MB-66519
Change-Id: I9a73ab538a24974535c975f4826fcf707559e2b8
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19765
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.5.query.sqlpp
new file mode 100644
index 0000000..96c263b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.5.query.sqlpp
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties:( c_w_id ,c_d_id, c_id)
+--  and requirement properties: (o_c_id, o_w_id, o_d_id)
+--  with the following join condition
+--  the expected final required properties of orders will be: (o_w_id, o_d_id, o_c_id)
+
+drop dataverse test if exists;
+create dataverse test;
+use test;
+
+
+
+CREATE TYPE cust_type AS {c_w_id : integer,c_d_id : integer, c_id : integer};
+CREATE DATASET customer(cust_type) PRIMARY KEY c_w_id, c_d_id, c_id;
+
+CREATE TYPE orders_type AS {o_w_id : integer, o_d_id : integer, o_id : integer};
+CREATE DATASET orders(orders_type) PRIMARY KEY o_w_id, o_d_id, o_id;
+
+
+
+
+
+SELECT c.c_id, o.*
+FROM customer c LEFT OUTER JOIN orders o ON (
+    c.c_w_id  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.7.query.sqlpp
new file mode 100644
index 0000000..bb0a1be
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.7.query.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+use test;
+
+
+
+SELECT c.c_id, o.*
+FROM customer c LEFT OUTER JOIN orders o ON (
+    c.c_w_id /*+ hash-bcast */  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.9.query.sqlpp
new file mode 100644
index 0000000..c0ed3de
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange/hash_join_exchange.9.query.sqlpp
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties: (o_w_id, o_d_id, o_id)
+--  and requirement properties: (o_w_id, o_d_id, o_id)
+--  with the following join condition
+--  the expected final required properties of orders will be: (o_w_id, o_d_id, o_id)
+
+
+use test;
+
+
+
+SELECT o.o_d_id, o.o_w_id, o.o_id, o.c_id, o.o_carrier_id, o.o_ol_cnt, c.*
+FROM orders o LEFT OUTER JOIN customer c ON (
+    c.c_w_id  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8);
+
+
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.sqlpp
new file mode 100644
index 0000000..9f01f47
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.sqlpp
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties: (a_1, a_0, a_2)
+--  with join condition a_0=b_0, a_1=b_1, a_2=b_2 and equivalence mapping a_0=a_1
+--  the expected final required properties of B will be: (b_0, b_1, b_2) which should be okay.
+
+drop dataverse test if exists;
+create dataverse test;
+use test;
+
+CREATE TYPE A_TYPE AS {a_0 : integer, a_1 : integer, a_2 : integer};
+CREATE DATASET A(A_TYPE) PRIMARY KEY a_0, a_1, a_2;
+
+CREATE TYPE B_TYPE AS {b_0 : integer, b_1 : integer, b_2 : integer};
+CREATE DATASET B(B_TYPE) PRIMARY KEY b_0, b_1, b_2;
+
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_2 = B.b_2)
+WHERE  A.a_0=A.a_2;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.sqlpp
new file mode 100644
index 0000000..9e2fbe3
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.sqlpp
@@ -0,0 +1,36 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first partition properties:( a_0, a_1, a_2)
+--  and requirement properties: (b_0, b_1, b_2)
+--  with the join condition A.a_0  = B.b_0 AND B.b_1 = A.a_1 AND A.a_2 = B.b_2 AND A.a_3 = B.b_3
+--  No extra hash exchanges are required as its already partitioned on (a_0, a_1, a_2, a_3) & (b_0, b_1, b_2, b_3)
+--  As partition on (a_0, a_1, a_2) implies partition on (a_0, a_1, a_2, a_3)
+
+
+use test;
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+        FROM  A  LEFT OUTER JOIN B   ON (
+            A.a_0  = B.b_0
+                AND B.b_1 = A.a_1
+                AND A.a_2 = B.b_2
+                AND A.a_3 = B.b_3);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.sqlpp
new file mode 100644
index 0000000..ace3520
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.sqlpp
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties: (a_1, a_0, a_2)
+--  with join condition a_0=b_0, a_1=b_1, a_2=b_2
+--  the expected final required properties of B will be: (b_0, b_1, b_2) which should be okay.
+
+
+
+drop dataverse test if exists;
+create dataverse test;
+use test;
+
+
+
+CREATE TYPE A_TYPE AS {a_0 : integer, a_1 : integer, a_2 : integer};
+CREATE DATASET A(A_TYPE) PRIMARY KEY a_0, a_1, a_2;
+
+CREATE TYPE B_TYPE AS {b_0 : integer, b_1 : integer, b_2 : integer};
+CREATE DATASET B(B_TYPE) PRIMARY KEY b_0, b_1, b_2;
+
+
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_2 = B.b_2);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.sqlpp
new file mode 100644
index 0000000..1a364fc
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties:( a_3 ,a_0, a_1)
+--  and requirement properties: (b_3, b_0, b_1)
+--  with the following join condition
+--  the expected final required properties of orders will be: (b_3, b_0, b_1)
+
+use test;
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B  ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_3 = B.b_3);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.sqlpp
new file mode 100644
index 0000000..1aff08c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.sqlpp
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties:( a_0, a_1)
+--  and requirement properties: (b_0, b_1)
+--  with the following join condition
+--  the expected final required properties of orders will be: (b_0, b_1)
+use test;
+
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1);
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.5.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.5.query.plan
new file mode 100644
index 0000000..d1c3b5a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.5.query.plan
@@ -0,0 +1,42 @@
+distribute result [$$43] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$43]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$43] <- [object-concat-strict({"c_id": $$46}, if-missing-or-null(cast(to-object($$o)), cast({  })))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$46, $$o]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$46, $$51), eq($$44, $$47), eq($$45, $$48))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$46, $$44, $$45][$$51, $$47, $$48]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$46, $$44, $$45]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    data-scan []<-[$$44, $$45, $$46, $$c] <- test.customer [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- DATASOURCE_SCAN  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$47, $$48, $$51]  |PARTITIONED|
+                select (gt($$o.getField("o_carrier_id"), 8)) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  assign [$$51] <- [$$o.getField("o_c_id")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    project ([$$47, $$48, $$o]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$47, $$48, $$49, $$o] <- test.orders [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.7.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.7.query.plan
new file mode 100644
index 0000000..cc77d9f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.7.query.plan
@@ -0,0 +1,42 @@
+distribute result [$$43] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$43]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$43] <- [object-concat-strict({"c_id": $$46}, if-missing-or-null(cast(to-object($$o)), cast({  })))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$46, $$o]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$46, $$51), eq($$44, $$47), eq($$45, $$48))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$46, $$44, $$45][$$51, $$47, $$48]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$46, $$44, $$45]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    data-scan []<-[$$44, $$45, $$46, $$c] <- test.customer [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- DATASOURCE_SCAN  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- BROADCAST_EXCHANGE  |PARTITIONED|
+                select (gt($$o.getField("o_carrier_id"), 8)) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  assign [$$51] <- [$$o.getField("o_c_id")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    project ([$$47, $$48, $$o]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$47, $$48, $$49, $$o] <- test.orders [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.9.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.9.query.plan
new file mode 100644
index 0000000..148740d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange/hash_join_exchange.9.query.plan
@@ -0,0 +1,60 @@
+distribute result [$$48] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$48]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$48] <- [object-concat-strict({"o_d_id": $$50, "o_w_id": $$49, "o_id": $$51, "c_id": $$65, "o_carrier_id": $$61, "o_ol_cnt": $$67}, if-missing-or-null(cast(to-object($$c)), cast({  })))] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$50, $$49, $$51, $$65, $$61, $$67, $$c]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$49, $$73), eq($$50, $$74), eq($$51, $$75))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$49, $$50, $$51][$$73, $$74, $$75]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$50, $$49, $$51, $$65, $$61, $$67]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$67, $$61, $$65] <- [$$o.getField("o_ol_cnt"), $$o.getField("o_carrier_id"), $$o.getField("c_id")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$49, $$50, $$51, $$o] <- test.orders [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$73, $$74, $$75]  |PARTITIONED|
+                project ([$$c, $$73, $$74, $$75]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    join (and(eq($$54, $$68), eq($$52, $$73), eq($$53, $$74))) [cardinality: 1000000.0, op-cost: 2000000.0, total-cost: 6000000.0]
+                    -- HYBRID_HASH_JOIN [$$68, $$73, $$74][$$54, $$52, $$53]  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- HASH_PARTITION_EXCHANGE [$$73, $$74, $$68]  |PARTITIONED|
+                        project ([$$73, $$74, $$75, $$68]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- STREAM_PROJECT  |PARTITIONED|
+                          select (gt($$69.getField("o_carrier_id"), 8)) [cardinality: 1000000.0, op-cost: 0.0, total-cost: 1000000.0]
+                          -- STREAM_SELECT  |PARTITIONED|
+                            assign [$$68] <- [$$69.getField("o_c_id")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- ASSIGN  |PARTITIONED|
+                              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                data-scan []<-[$$73, $$74, $$75, $$69] <- test.orders [cardinality: 1000000.0, op-cost: 1000000.0, total-cost: 1000000.0]
+                                -- DATASOURCE_SCAN  |PARTITIONED|
+                                  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$52, $$53, $$54, $$c] <- test.customer [cardinality: 1000000.0, op-cost: 1000000.0, total-cost: 1000000.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.plan
new file mode 100644
index 0000000..094f7cf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.11.query.plan
@@ -0,0 +1,44 @@
+distribute result [$$44] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$44]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$44] <- [{"a_0": $$47, "a_1": $$48, "a_2": $$49, "a_3": $$60, "b_3": $$61}] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$47, $$48, $$49, $$60, $$61]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$49, $$52), eq($$47, $$50), eq($$51, $$48))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$49, $$47, $$48][$$52, $$50, $$51]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                select (eq($$47, $$49)) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  project ([$$47, $$48, $$49, $$60]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- STREAM_PROJECT  |PARTITIONED|
+                    assign [$$60] <- [$$A.getField("a_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ASSIGN  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$47, $$48, $$49, $$A] <- test.A [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$61, $$52, $$50, $$51]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$61] <- [$$B.getField("b_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$50, $$51, $$52, $$B] <- test.B [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.plan
new file mode 100644
index 0000000..09f76b6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.13.query.plan
@@ -0,0 +1,42 @@
+distribute result [$$44] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$44]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$44] <- [{"a_0": $$45, "a_1": $$46, "a_2": $$47, "a_3": $$53, "b_3": $$54}] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$45, $$46, $$47, $$53, $$54]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$47, $$50), eq($$53, $$54), eq($$45, $$48), eq($$49, $$46))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$47, $$53, $$45, $$46][$$50, $$54, $$48, $$49]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$45, $$46, $$47, $$53]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$53] <- [$$A.getField("a_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$45, $$46, $$47, $$A] <- test.A [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$54, $$50, $$48, $$49]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$54] <- [$$B.getField("b_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$48, $$49, $$50, $$B] <- test.B [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.plan
new file mode 100644
index 0000000..b51190e
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.5.query.plan
@@ -0,0 +1,42 @@
+distribute result [$$41] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$41]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$41] <- [{"a_0": $$42, "a_1": $$43, "a_2": $$44, "a_3": $$57, "b_3": $$58}] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$42, $$43, $$44, $$57, $$58]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$44, $$47), eq($$42, $$45), eq($$46, $$43))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$44, $$42, $$43][$$47, $$45, $$46]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$42, $$43, $$44, $$57]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$57] <- [$$A.getField("a_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$42, $$43, $$44, $$A] <- test.A [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                project ([$$58, $$47, $$45, $$46]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$58] <- [$$B.getField("b_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$45, $$46, $$47, $$B] <- test.B [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.plan
new file mode 100644
index 0000000..6fe29dd
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.7.query.plan
@@ -0,0 +1,44 @@
+distribute result [$$41] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$41]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$41] <- [{"a_0": $$42, "a_1": $$43, "a_2": $$44, "a_3": $$48, "b_3": $$49}] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$42, $$43, $$44, $$48, $$49]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$48, $$49), eq($$42, $$45), eq($$46, $$43))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$48, $$42, $$43][$$49, $$45, $$46]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$48, $$42, $$43]  |PARTITIONED|
+                project ([$$42, $$43, $$44, $$48]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$48] <- [$$A.getField("a_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$42, $$43, $$44, $$A] <- test.A [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$49, $$45, $$46]  |PARTITIONED|
+                project ([$$49, $$45, $$46]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$49] <- [$$B.getField("b_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    project ([$$45, $$46, $$B]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$45, $$46, $$47, $$B] <- test.B [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.plan
new file mode 100644
index 0000000..37b0111
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/joins/hash_join_exchange_1/hash_join_exchange_1.9.query.plan
@@ -0,0 +1,44 @@
+distribute result [$$38] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$38]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$38] <- [{"a_0": $$39, "a_1": $$40, "a_2": $$41, "a_3": $$52, "b_3": $$53}] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+      -- ASSIGN  |PARTITIONED|
+        project ([$$39, $$40, $$41, $$52, $$53]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+        -- STREAM_PROJECT  |PARTITIONED|
+          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+            left outer join (and(eq($$39, $$42), eq($$43, $$40))) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- HYBRID_HASH_JOIN [$$39, $$40][$$42, $$43]  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$39, $$40]  |PARTITIONED|
+                project ([$$39, $$40, $$41, $$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$52] <- [$$A.getField("a_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      data-scan []<-[$$39, $$40, $$41, $$A] <- test.A [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- DATASOURCE_SCAN  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                          empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
+              exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+              -- HASH_PARTITION_EXCHANGE [$$42, $$43]  |PARTITIONED|
+                project ([$$53, $$42, $$43]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_PROJECT  |PARTITIONED|
+                  assign [$$53] <- [$$B.getField("b_3")] [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- ASSIGN  |PARTITIONED|
+                    project ([$$42, $$43, $$B]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- STREAM_PROJECT  |PARTITIONED|
+                      exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                        data-scan []<-[$$42, $$43, $$44, $$B] <- test.B [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                        -- DATASOURCE_SCAN  |PARTITIONED|
+                          exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                            empty-tuple-source [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                            -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.1.ddl.sqlpp
new file mode 100644
index 0000000..2b817b2
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.1.ddl.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+CREATE TYPE cust_type AS {c_w_id : integer,c_d_id : integer, c_id : integer};
+CREATE DATASET customer(cust_type) PRIMARY KEY c_w_id, c_d_id, c_id;
+
+CREATE TYPE orders_type AS {o_w_id : integer, o_d_id : integer, o_id : integer};
+CREATE DATASET orders(orders_type) PRIMARY KEY o_w_id, o_d_id, o_id;
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.2.update.sqlpp
new file mode 100644
index 0000000..b7042c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.2.update.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+INSERT INTO customer ({"c_id":2,"c_d_id":1,"c_w_id":1,"c_discount":0.3413,"c_credit":"GC","c_first":"jqmoaqly","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":4228533.790000002,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"ingngcunkfzoku","c_street_2":"sihqvjfhhphyd","c_city":"jtflodpnsmtn","c_state":"ps","c_zip":"640711111","c_phone":"8709221552477813","c_since":"2016-04-28 05:24:17","key":"1.1.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":2,"c_w_id":1,"c_discount":0.4472,"c_credit":"GC","c_first":"yupforh","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":2637344.5900000003,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"kfmblhsbdf","c_street_2":"ldmnvpzyzqpjgdldcxd","c_city":"jakerbxcuwmnfvqntd","c_state":"ft","c_zip":"377611111","c_phone":"2594721341099812","c_since":"2019-07-23 11:35:08","key":"1.2.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":3,"c_w_id":1,"c_discount":0.4549,"c_credit":"GC","c_first":"cdchdqts","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":2275392.5500000003,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"emwtajlhjyzigbxtll","c_street_2":"bsxxckrjkxvnx","c_city":"vuthjjchaubyurhxu","c_state":"ae","c_zip":"207611111","c_phone":"2223483665383895","c_since":"2015-01-10 09:13:11","key":"1.3.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":4,"c_w_id":1,"c_discount":0.2902,"c_credit":"BC","c_first":"dbxdynrsxq","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":-10.0,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"cdnrcokhecssszdxy","c_street_2":"owkrknkjxxeh","c_city":"dizrkudapnhlur","c_state":"ne","c_zip":"214211111","c_phone":"9527105621833116","c_since":"2019-09-30 04:41:34","key":"1.4.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":5,"c_w_id":1,"c_discount":0.1233,"c_credit":"GC","c_first":"xvaijyn","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":8318309.3500000015,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"tbjodrhupcwdyhiul","c_street_2":"fvbhntiihftymwkabbr","c_city":"cjlfuvcqvkg","c_state":"ng","c_zip":"417111111","c_phone":"5673180500149103","c_since":"2014-10-20 20:06:32","key":"1.5.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":6,"c_w_id":1,"c_discount":0.4838,"c_credit":"GC","c_first":"ikjojneh","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":6788632.1400000015,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"djtnknodfrkny","c_street_2":"iwarlwscxj","c_city":"eaixkpruvqiuhbqnpasf","c_state":"nl","c_zip":"461611111","c_phone":"7068147293579806","c_since":"2018-08-29 23:19:39","key":"1.6.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":7,"c_w_id":1,"c_discount":0.3469,"c_credit":"GC","c_first":"mqvlqra","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":-10.0,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"rcnecgnkrtpgkzrjap","c_street_2":"voabsdpbfuxusyiqs","c_city":"udtfebydicf","c_state":"am","c_zip":"528111111","c_phone":"3972476735934659","c_since":"2020-02-17 05:48:03","key":"1.7.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":8,"c_w_id":1,"c_discount":0.319,"c_credit":"GC","c_first":"hcmxoxvr","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":3253865.68,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"jrkgfusmekbbjygma","c_street_2":"ljkmhxyzyctxyrtlo","c_city":"uxceeygbpgjh","c_state":"he","c_zip":"111611111","c_phone":"3276574132758646","c_since":"2019-08-03 09:05:23","key":"1.8.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":9,"c_w_id":1,"c_discount":0.1118,"c_credit":"BC","c_first":"horovuvbi","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":-10.0,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"ikznzllcvcejsvsf","c_street_2":"lmaferykvzvnlck","c_city":"xzqhxyxkfwgd","c_state":"eu","c_zip":"130211111","c_phone":"3913647178718934","c_since":"2018-07-13 11:47:36","key":"1.9.2"});
+INSERT INTO customer ({"c_id":2,"c_d_id":10,"c_w_id":1,"c_discount":0.4349,"c_credit":"GC","c_first":"ghoevrth","c_middle":"OE","c_last":"BARBAROUGHT","c_credit_lim":50000.0,"c_balance":-10.0,"c_ytd_payment":10.0,"c_payment_cnt":1,"c_delivery_cnt":0,"c_street_1":"qvyasidzgzxzkf","c_street_2":"yyqlehcjsyoaf","c_city":"svnmulhzkp","c_state":"ni","c_zip":"302111111","c_phone":"0212917484161389","c_since":"2017-04-29 18:46:22","key":"1.10.2"});
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.3.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.3.update.sqlpp
new file mode 100644
index 0000000..5a27699
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.3.update.sqlpp
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+INSERT INTO orders ({"o_id":881,"o_c_id":2,"o_d_id":1,"o_w_id":1,"o_carrier_id":6,"o_ol_cnt":11});
+INSERT INTO orders ({"o_id":525,"o_c_id":2,"o_d_id":2,"o_w_id":1,"o_carrier_id":1,"o_ol_cnt":15});
+INSERT INTO orders ({"o_id":466,"o_c_id":2,"o_d_id":3,"o_w_id":1,"o_carrier_id":10,"o_ol_cnt":14});
+INSERT INTO orders ({"o_id":2449,"o_c_id":2,"o_d_id":4,"o_w_id":1,"o_carrier_id":0,"o_ol_cnt":15});
+INSERT INTO orders ({"o_id":1652,"o_c_id":2,"o_d_id":5,"o_w_id":1,"o_carrier_id":10,"o_ol_cnt":7});
+INSERT INTO orders ({"o_id":1350,"o_c_id":2,"o_d_id":6,"o_w_id":1,"o_carrier_id":8,"o_ol_cnt":9});
+INSERT INTO orders ({"o_id":2822,"o_c_id":2,"o_d_id":7,"o_w_id":1,"o_carrier_id":0,"o_ol_cnt":12});
+INSERT INTO orders ({"o_id":677,"o_c_id":2,"o_d_id":8,"o_w_id":1,"o_carrier_id":6,"o_ol_cnt":6});
+INSERT INTO orders ({"o_id":2851,"o_c_id":2,"o_d_id":9,"o_w_id":1,"o_carrier_id":0,"o_ol_cnt":12});
+INSERT INTO orders ({"o_id":2841,"o_c_id":2,"o_d_id":10,"o_w_id":1,"o_carrier_id":0,"o_ol_cnt":9});
+
+
+
+
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.4.query.sqlpp
new file mode 100644
index 0000000..a878aa5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.4.query.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+
+
+SELECT c.c_id, o.*
+FROM customer c LEFT OUTER JOIN orders o ON (
+    c.c_w_id  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8)
+ORDER BY o.o_d_id, o.o_id;
+
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.6.query.sqlpp
new file mode 100644
index 0000000..f9572a1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.6.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT c.c_id, o.*
+FROM customer c LEFT OUTER JOIN orders o ON (
+    c.c_w_id /*+ hash-bcast */  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8)
+ORDER BY o.o_d_id, o.o_id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.8.query.sqlpp
new file mode 100644
index 0000000..c77148b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange/hash_join_exchange.8.query.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+
+SELECT o.o_d_id, o.o_w_id, o.o_id, o.c_id, o.o_carrier_id, o.o_ol_cnt, c.*
+FROM orders o LEFT OUTER JOIN customer c ON (
+    c.c_w_id  = o.o_w_id
+        AND c.c_d_id = o.o_d_id
+        AND c.c_id = o.o_c_id
+        AND o.o_carrier_id > 8)
+ORDER BY o.o_d_id, o.o_w_id, o.o_id, o.c_id, o.o_carrier_id, o.o_ol_cnt;
+
+
+
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.1.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.1.ddl.sqlpp
new file mode 100644
index 0000000..c12c22d
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.1.ddl.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+CREATE TYPE A_TYPE AS {a_0 : integer, a_1 : integer, a_2 : integer};
+CREATE DATASET A(A_TYPE) PRIMARY KEY a_0, a_1, a_2;
+
+CREATE TYPE B_TYPE AS {b_0 : integer, b_1 : integer, b_2 : integer};
+CREATE DATASET B(B_TYPE) PRIMARY KEY b_0, b_1, b_2;
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.10.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.10.query.sqlpp
new file mode 100644
index 0000000..07bf929
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.10.query.sqlpp
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties: (a_1, a_0, a_2)
+--  with join condition a_0=b_0, a_1=b_1, a_2=b_2 and equivalence mapping a_0=a_1
+--  the expected final required properties of B will be: (b_0, b_1, b_2) which should be okay.
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_2 = B.b_2)
+WHERE  A.a_0=A.a_2
+ORDER BY  A.a_0, A.a_1, A.a_2, A.a_3, B.b_3;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.12.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.12.query.sqlpp
new file mode 100644
index 0000000..42a62ef
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.12.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_2 = B.b_2
+        AND A.a_3 = B.b_3)
+ORDER BY  A.a_0, A.a_1, A.a_2, A.a_3, B.b_3;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.2.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.2.update.sqlpp
new file mode 100644
index 0000000..c8f69c8
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.2.update.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+INSERT INTO A(
+    SELECT VALUE    {"a_0": i, "a_1": i, "a_2": i, "a_3": i}
+    FROM range(1, 100) i
+);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.3.update.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.3.update.sqlpp
new file mode 100644
index 0000000..074d554
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.3.update.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+
+INSERT INTO B(
+    SELECT VALUE    {"b_0": i, "b_1": i, "b_2": i, "b_3": i}
+    FROM range(51, 150) i
+);
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.4.query.sqlpp
new file mode 100644
index 0000000..c50b573
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.4.query.sqlpp
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+--  This Test is intended to test the IPartitioningRequirementsCoordinator for outer join
+--  in the case where the first delivered properties: (a_1, a_0, a_2)
+--  with join condition a_0=b_0, a_1=b_1, a_2=b_2
+--  the expected final required properties of B will be: (b_0, b_1, b_2) which should be okay.
+
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1
+        AND A.a_2 = B.b_2)
+ORDER BY  A.a_0, A.a_1, A.a_2, A.a_3, B.b_3;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.6.query.sqlpp
new file mode 100644
index 0000000..b7d7196
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.6.query.sqlpp
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B  ON (
+        A.a_0  = B.b_0
+            AND B.b_1 = A.a_1
+            AND A.a_3 = B.b_3)
+ORDER BY  A.a_0, A.a_1, A.a_2, A.a_3, B.b_3;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.8.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.8.query.sqlpp
new file mode 100644
index 0000000..605e70f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/join/hash_join_exchange_1/hash_join_exchange_1.8.query.sqlpp
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT A.a_0, A.a_1, A.a_2, A.a_3, B.b_3
+FROM  A  LEFT OUTER JOIN B   ON (
+    A.a_0  = B.b_0
+        AND B.b_1 = A.a_1)
+ORDER BY  A.a_0, A.a_1, A.a_2, A.a_3, B.b_3;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.4.adm
new file mode 100644
index 0000000..097f737
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.4.adm
@@ -0,0 +1,10 @@
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "o_w_id": 1, "o_d_id": 3, "o_id": 466, "o_c_id": 2, "o_carrier_id": 10, "o_ol_cnt": 14, "c_id": 2 }
+{ "o_w_id": 1, "o_d_id": 5, "o_id": 1652, "o_c_id": 2, "o_carrier_id": 10, "o_ol_cnt": 7, "c_id": 2 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.6.adm
new file mode 100644
index 0000000..097f737
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.6.adm
@@ -0,0 +1,10 @@
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "c_id": 2 }
+{ "o_w_id": 1, "o_d_id": 3, "o_id": 466, "o_c_id": 2, "o_carrier_id": 10, "o_ol_cnt": 14, "c_id": 2 }
+{ "o_w_id": 1, "o_d_id": 5, "o_id": 1652, "o_c_id": 2, "o_carrier_id": 10, "o_ol_cnt": 7, "c_id": 2 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.8.adm
new file mode 100644
index 0000000..113d305
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange/hash_join_exchange.8.adm
@@ -0,0 +1,10 @@
+{ "o_d_id": 1, "o_w_id": 1, "o_id": 881, "o_carrier_id": 6, "o_ol_cnt": 11 }
+{ "o_d_id": 2, "o_w_id": 1, "o_id": 525, "o_carrier_id": 1, "o_ol_cnt": 15 }
+{ "c_w_id": 1, "c_d_id": 3, "c_id": 2, "c_discount": 0.4549, "c_credit": "GC", "c_first": "cdchdqts", "c_middle": "OE", "c_last": "BARBAROUGHT", "c_credit_lim": 50000.0, "c_balance": 2275392.5500000003, "c_ytd_payment": 10.0, "c_payment_cnt": 1, "c_delivery_cnt": 0, "c_street_1": "emwtajlhjyzigbxtll", "c_street_2": "bsxxckrjkxvnx", "c_city": "vuthjjchaubyurhxu", "c_state": "ae", "c_zip": "207611111", "c_phone": "2223483665383895", "c_since": "2015-01-10 09:13:11", "key": "1.3.2", "o_d_id": 3, "o_w_id": 1, "o_id": 466, "o_carrier_id": 10, "o_ol_cnt": 14 }
+{ "o_d_id": 4, "o_w_id": 1, "o_id": 2449, "o_carrier_id": 0, "o_ol_cnt": 15 }
+{ "c_w_id": 1, "c_d_id": 5, "c_id": 2, "c_discount": 0.1233, "c_credit": "GC", "c_first": "xvaijyn", "c_middle": "OE", "c_last": "BARBAROUGHT", "c_credit_lim": 50000.0, "c_balance": 8318309.3500000015, "c_ytd_payment": 10.0, "c_payment_cnt": 1, "c_delivery_cnt": 0, "c_street_1": "tbjodrhupcwdyhiul", "c_street_2": "fvbhntiihftymwkabbr", "c_city": "cjlfuvcqvkg", "c_state": "ng", "c_zip": "417111111", "c_phone": "5673180500149103", "c_since": "2014-10-20 20:06:32", "key": "1.5.2", "o_d_id": 5, "o_w_id": 1, "o_id": 1652, "o_carrier_id": 10, "o_ol_cnt": 7 }
+{ "o_d_id": 6, "o_w_id": 1, "o_id": 1350, "o_carrier_id": 8, "o_ol_cnt": 9 }
+{ "o_d_id": 7, "o_w_id": 1, "o_id": 2822, "o_carrier_id": 0, "o_ol_cnt": 12 }
+{ "o_d_id": 8, "o_w_id": 1, "o_id": 677, "o_carrier_id": 6, "o_ol_cnt": 6 }
+{ "o_d_id": 9, "o_w_id": 1, "o_id": 2851, "o_carrier_id": 0, "o_ol_cnt": 12 }
+{ "o_d_id": 10, "o_w_id": 1, "o_id": 2841, "o_carrier_id": 0, "o_ol_cnt": 9 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.10.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.10.adm
new file mode 100644
index 0000000..497b339
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.10.adm
@@ -0,0 +1,100 @@
+{ "a_0": 1, "a_1": 1, "a_2": 1, "a_3": 1 }
+{ "a_0": 2, "a_1": 2, "a_2": 2, "a_3": 2 }
+{ "a_0": 3, "a_1": 3, "a_2": 3, "a_3": 3 }
+{ "a_0": 4, "a_1": 4, "a_2": 4, "a_3": 4 }
+{ "a_0": 5, "a_1": 5, "a_2": 5, "a_3": 5 }
+{ "a_0": 6, "a_1": 6, "a_2": 6, "a_3": 6 }
+{ "a_0": 7, "a_1": 7, "a_2": 7, "a_3": 7 }
+{ "a_0": 8, "a_1": 8, "a_2": 8, "a_3": 8 }
+{ "a_0": 9, "a_1": 9, "a_2": 9, "a_3": 9 }
+{ "a_0": 10, "a_1": 10, "a_2": 10, "a_3": 10 }
+{ "a_0": 11, "a_1": 11, "a_2": 11, "a_3": 11 }
+{ "a_0": 12, "a_1": 12, "a_2": 12, "a_3": 12 }
+{ "a_0": 13, "a_1": 13, "a_2": 13, "a_3": 13 }
+{ "a_0": 14, "a_1": 14, "a_2": 14, "a_3": 14 }
+{ "a_0": 15, "a_1": 15, "a_2": 15, "a_3": 15 }
+{ "a_0": 16, "a_1": 16, "a_2": 16, "a_3": 16 }
+{ "a_0": 17, "a_1": 17, "a_2": 17, "a_3": 17 }
+{ "a_0": 18, "a_1": 18, "a_2": 18, "a_3": 18 }
+{ "a_0": 19, "a_1": 19, "a_2": 19, "a_3": 19 }
+{ "a_0": 20, "a_1": 20, "a_2": 20, "a_3": 20 }
+{ "a_0": 21, "a_1": 21, "a_2": 21, "a_3": 21 }
+{ "a_0": 22, "a_1": 22, "a_2": 22, "a_3": 22 }
+{ "a_0": 23, "a_1": 23, "a_2": 23, "a_3": 23 }
+{ "a_0": 24, "a_1": 24, "a_2": 24, "a_3": 24 }
+{ "a_0": 25, "a_1": 25, "a_2": 25, "a_3": 25 }
+{ "a_0": 26, "a_1": 26, "a_2": 26, "a_3": 26 }
+{ "a_0": 27, "a_1": 27, "a_2": 27, "a_3": 27 }
+{ "a_0": 28, "a_1": 28, "a_2": 28, "a_3": 28 }
+{ "a_0": 29, "a_1": 29, "a_2": 29, "a_3": 29 }
+{ "a_0": 30, "a_1": 30, "a_2": 30, "a_3": 30 }
+{ "a_0": 31, "a_1": 31, "a_2": 31, "a_3": 31 }
+{ "a_0": 32, "a_1": 32, "a_2": 32, "a_3": 32 }
+{ "a_0": 33, "a_1": 33, "a_2": 33, "a_3": 33 }
+{ "a_0": 34, "a_1": 34, "a_2": 34, "a_3": 34 }
+{ "a_0": 35, "a_1": 35, "a_2": 35, "a_3": 35 }
+{ "a_0": 36, "a_1": 36, "a_2": 36, "a_3": 36 }
+{ "a_0": 37, "a_1": 37, "a_2": 37, "a_3": 37 }
+{ "a_0": 38, "a_1": 38, "a_2": 38, "a_3": 38 }
+{ "a_0": 39, "a_1": 39, "a_2": 39, "a_3": 39 }
+{ "a_0": 40, "a_1": 40, "a_2": 40, "a_3": 40 }
+{ "a_0": 41, "a_1": 41, "a_2": 41, "a_3": 41 }
+{ "a_0": 42, "a_1": 42, "a_2": 42, "a_3": 42 }
+{ "a_0": 43, "a_1": 43, "a_2": 43, "a_3": 43 }
+{ "a_0": 44, "a_1": 44, "a_2": 44, "a_3": 44 }
+{ "a_0": 45, "a_1": 45, "a_2": 45, "a_3": 45 }
+{ "a_0": 46, "a_1": 46, "a_2": 46, "a_3": 46 }
+{ "a_0": 47, "a_1": 47, "a_2": 47, "a_3": 47 }
+{ "a_0": 48, "a_1": 48, "a_2": 48, "a_3": 48 }
+{ "a_0": 49, "a_1": 49, "a_2": 49, "a_3": 49 }
+{ "a_0": 50, "a_1": 50, "a_2": 50, "a_3": 50 }
+{ "a_0": 51, "a_1": 51, "a_2": 51, "a_3": 51, "b_3": 51 }
+{ "a_0": 52, "a_1": 52, "a_2": 52, "a_3": 52, "b_3": 52 }
+{ "a_0": 53, "a_1": 53, "a_2": 53, "a_3": 53, "b_3": 53 }
+{ "a_0": 54, "a_1": 54, "a_2": 54, "a_3": 54, "b_3": 54 }
+{ "a_0": 55, "a_1": 55, "a_2": 55, "a_3": 55, "b_3": 55 }
+{ "a_0": 56, "a_1": 56, "a_2": 56, "a_3": 56, "b_3": 56 }
+{ "a_0": 57, "a_1": 57, "a_2": 57, "a_3": 57, "b_3": 57 }
+{ "a_0": 58, "a_1": 58, "a_2": 58, "a_3": 58, "b_3": 58 }
+{ "a_0": 59, "a_1": 59, "a_2": 59, "a_3": 59, "b_3": 59 }
+{ "a_0": 60, "a_1": 60, "a_2": 60, "a_3": 60, "b_3": 60 }
+{ "a_0": 61, "a_1": 61, "a_2": 61, "a_3": 61, "b_3": 61 }
+{ "a_0": 62, "a_1": 62, "a_2": 62, "a_3": 62, "b_3": 62 }
+{ "a_0": 63, "a_1": 63, "a_2": 63, "a_3": 63, "b_3": 63 }
+{ "a_0": 64, "a_1": 64, "a_2": 64, "a_3": 64, "b_3": 64 }
+{ "a_0": 65, "a_1": 65, "a_2": 65, "a_3": 65, "b_3": 65 }
+{ "a_0": 66, "a_1": 66, "a_2": 66, "a_3": 66, "b_3": 66 }
+{ "a_0": 67, "a_1": 67, "a_2": 67, "a_3": 67, "b_3": 67 }
+{ "a_0": 68, "a_1": 68, "a_2": 68, "a_3": 68, "b_3": 68 }
+{ "a_0": 69, "a_1": 69, "a_2": 69, "a_3": 69, "b_3": 69 }
+{ "a_0": 70, "a_1": 70, "a_2": 70, "a_3": 70, "b_3": 70 }
+{ "a_0": 71, "a_1": 71, "a_2": 71, "a_3": 71, "b_3": 71 }
+{ "a_0": 72, "a_1": 72, "a_2": 72, "a_3": 72, "b_3": 72 }
+{ "a_0": 73, "a_1": 73, "a_2": 73, "a_3": 73, "b_3": 73 }
+{ "a_0": 74, "a_1": 74, "a_2": 74, "a_3": 74, "b_3": 74 }
+{ "a_0": 75, "a_1": 75, "a_2": 75, "a_3": 75, "b_3": 75 }
+{ "a_0": 76, "a_1": 76, "a_2": 76, "a_3": 76, "b_3": 76 }
+{ "a_0": 77, "a_1": 77, "a_2": 77, "a_3": 77, "b_3": 77 }
+{ "a_0": 78, "a_1": 78, "a_2": 78, "a_3": 78, "b_3": 78 }
+{ "a_0": 79, "a_1": 79, "a_2": 79, "a_3": 79, "b_3": 79 }
+{ "a_0": 80, "a_1": 80, "a_2": 80, "a_3": 80, "b_3": 80 }
+{ "a_0": 81, "a_1": 81, "a_2": 81, "a_3": 81, "b_3": 81 }
+{ "a_0": 82, "a_1": 82, "a_2": 82, "a_3": 82, "b_3": 82 }
+{ "a_0": 83, "a_1": 83, "a_2": 83, "a_3": 83, "b_3": 83 }
+{ "a_0": 84, "a_1": 84, "a_2": 84, "a_3": 84, "b_3": 84 }
+{ "a_0": 85, "a_1": 85, "a_2": 85, "a_3": 85, "b_3": 85 }
+{ "a_0": 86, "a_1": 86, "a_2": 86, "a_3": 86, "b_3": 86 }
+{ "a_0": 87, "a_1": 87, "a_2": 87, "a_3": 87, "b_3": 87 }
+{ "a_0": 88, "a_1": 88, "a_2": 88, "a_3": 88, "b_3": 88 }
+{ "a_0": 89, "a_1": 89, "a_2": 89, "a_3": 89, "b_3": 89 }
+{ "a_0": 90, "a_1": 90, "a_2": 90, "a_3": 90, "b_3": 90 }
+{ "a_0": 91, "a_1": 91, "a_2": 91, "a_3": 91, "b_3": 91 }
+{ "a_0": 92, "a_1": 92, "a_2": 92, "a_3": 92, "b_3": 92 }
+{ "a_0": 93, "a_1": 93, "a_2": 93, "a_3": 93, "b_3": 93 }
+{ "a_0": 94, "a_1": 94, "a_2": 94, "a_3": 94, "b_3": 94 }
+{ "a_0": 95, "a_1": 95, "a_2": 95, "a_3": 95, "b_3": 95 }
+{ "a_0": 96, "a_1": 96, "a_2": 96, "a_3": 96, "b_3": 96 }
+{ "a_0": 97, "a_1": 97, "a_2": 97, "a_3": 97, "b_3": 97 }
+{ "a_0": 98, "a_1": 98, "a_2": 98, "a_3": 98, "b_3": 98 }
+{ "a_0": 99, "a_1": 99, "a_2": 99, "a_3": 99, "b_3": 99 }
+{ "a_0": 100, "a_1": 100, "a_2": 100, "a_3": 100, "b_3": 100 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.12.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.12.adm
new file mode 100644
index 0000000..497b339
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.12.adm
@@ -0,0 +1,100 @@
+{ "a_0": 1, "a_1": 1, "a_2": 1, "a_3": 1 }
+{ "a_0": 2, "a_1": 2, "a_2": 2, "a_3": 2 }
+{ "a_0": 3, "a_1": 3, "a_2": 3, "a_3": 3 }
+{ "a_0": 4, "a_1": 4, "a_2": 4, "a_3": 4 }
+{ "a_0": 5, "a_1": 5, "a_2": 5, "a_3": 5 }
+{ "a_0": 6, "a_1": 6, "a_2": 6, "a_3": 6 }
+{ "a_0": 7, "a_1": 7, "a_2": 7, "a_3": 7 }
+{ "a_0": 8, "a_1": 8, "a_2": 8, "a_3": 8 }
+{ "a_0": 9, "a_1": 9, "a_2": 9, "a_3": 9 }
+{ "a_0": 10, "a_1": 10, "a_2": 10, "a_3": 10 }
+{ "a_0": 11, "a_1": 11, "a_2": 11, "a_3": 11 }
+{ "a_0": 12, "a_1": 12, "a_2": 12, "a_3": 12 }
+{ "a_0": 13, "a_1": 13, "a_2": 13, "a_3": 13 }
+{ "a_0": 14, "a_1": 14, "a_2": 14, "a_3": 14 }
+{ "a_0": 15, "a_1": 15, "a_2": 15, "a_3": 15 }
+{ "a_0": 16, "a_1": 16, "a_2": 16, "a_3": 16 }
+{ "a_0": 17, "a_1": 17, "a_2": 17, "a_3": 17 }
+{ "a_0": 18, "a_1": 18, "a_2": 18, "a_3": 18 }
+{ "a_0": 19, "a_1": 19, "a_2": 19, "a_3": 19 }
+{ "a_0": 20, "a_1": 20, "a_2": 20, "a_3": 20 }
+{ "a_0": 21, "a_1": 21, "a_2": 21, "a_3": 21 }
+{ "a_0": 22, "a_1": 22, "a_2": 22, "a_3": 22 }
+{ "a_0": 23, "a_1": 23, "a_2": 23, "a_3": 23 }
+{ "a_0": 24, "a_1": 24, "a_2": 24, "a_3": 24 }
+{ "a_0": 25, "a_1": 25, "a_2": 25, "a_3": 25 }
+{ "a_0": 26, "a_1": 26, "a_2": 26, "a_3": 26 }
+{ "a_0": 27, "a_1": 27, "a_2": 27, "a_3": 27 }
+{ "a_0": 28, "a_1": 28, "a_2": 28, "a_3": 28 }
+{ "a_0": 29, "a_1": 29, "a_2": 29, "a_3": 29 }
+{ "a_0": 30, "a_1": 30, "a_2": 30, "a_3": 30 }
+{ "a_0": 31, "a_1": 31, "a_2": 31, "a_3": 31 }
+{ "a_0": 32, "a_1": 32, "a_2": 32, "a_3": 32 }
+{ "a_0": 33, "a_1": 33, "a_2": 33, "a_3": 33 }
+{ "a_0": 34, "a_1": 34, "a_2": 34, "a_3": 34 }
+{ "a_0": 35, "a_1": 35, "a_2": 35, "a_3": 35 }
+{ "a_0": 36, "a_1": 36, "a_2": 36, "a_3": 36 }
+{ "a_0": 37, "a_1": 37, "a_2": 37, "a_3": 37 }
+{ "a_0": 38, "a_1": 38, "a_2": 38, "a_3": 38 }
+{ "a_0": 39, "a_1": 39, "a_2": 39, "a_3": 39 }
+{ "a_0": 40, "a_1": 40, "a_2": 40, "a_3": 40 }
+{ "a_0": 41, "a_1": 41, "a_2": 41, "a_3": 41 }
+{ "a_0": 42, "a_1": 42, "a_2": 42, "a_3": 42 }
+{ "a_0": 43, "a_1": 43, "a_2": 43, "a_3": 43 }
+{ "a_0": 44, "a_1": 44, "a_2": 44, "a_3": 44 }
+{ "a_0": 45, "a_1": 45, "a_2": 45, "a_3": 45 }
+{ "a_0": 46, "a_1": 46, "a_2": 46, "a_3": 46 }
+{ "a_0": 47, "a_1": 47, "a_2": 47, "a_3": 47 }
+{ "a_0": 48, "a_1": 48, "a_2": 48, "a_3": 48 }
+{ "a_0": 49, "a_1": 49, "a_2": 49, "a_3": 49 }
+{ "a_0": 50, "a_1": 50, "a_2": 50, "a_3": 50 }
+{ "a_0": 51, "a_1": 51, "a_2": 51, "a_3": 51, "b_3": 51 }
+{ "a_0": 52, "a_1": 52, "a_2": 52, "a_3": 52, "b_3": 52 }
+{ "a_0": 53, "a_1": 53, "a_2": 53, "a_3": 53, "b_3": 53 }
+{ "a_0": 54, "a_1": 54, "a_2": 54, "a_3": 54, "b_3": 54 }
+{ "a_0": 55, "a_1": 55, "a_2": 55, "a_3": 55, "b_3": 55 }
+{ "a_0": 56, "a_1": 56, "a_2": 56, "a_3": 56, "b_3": 56 }
+{ "a_0": 57, "a_1": 57, "a_2": 57, "a_3": 57, "b_3": 57 }
+{ "a_0": 58, "a_1": 58, "a_2": 58, "a_3": 58, "b_3": 58 }
+{ "a_0": 59, "a_1": 59, "a_2": 59, "a_3": 59, "b_3": 59 }
+{ "a_0": 60, "a_1": 60, "a_2": 60, "a_3": 60, "b_3": 60 }
+{ "a_0": 61, "a_1": 61, "a_2": 61, "a_3": 61, "b_3": 61 }
+{ "a_0": 62, "a_1": 62, "a_2": 62, "a_3": 62, "b_3": 62 }
+{ "a_0": 63, "a_1": 63, "a_2": 63, "a_3": 63, "b_3": 63 }
+{ "a_0": 64, "a_1": 64, "a_2": 64, "a_3": 64, "b_3": 64 }
+{ "a_0": 65, "a_1": 65, "a_2": 65, "a_3": 65, "b_3": 65 }
+{ "a_0": 66, "a_1": 66, "a_2": 66, "a_3": 66, "b_3": 66 }
+{ "a_0": 67, "a_1": 67, "a_2": 67, "a_3": 67, "b_3": 67 }
+{ "a_0": 68, "a_1": 68, "a_2": 68, "a_3": 68, "b_3": 68 }
+{ "a_0": 69, "a_1": 69, "a_2": 69, "a_3": 69, "b_3": 69 }
+{ "a_0": 70, "a_1": 70, "a_2": 70, "a_3": 70, "b_3": 70 }
+{ "a_0": 71, "a_1": 71, "a_2": 71, "a_3": 71, "b_3": 71 }
+{ "a_0": 72, "a_1": 72, "a_2": 72, "a_3": 72, "b_3": 72 }
+{ "a_0": 73, "a_1": 73, "a_2": 73, "a_3": 73, "b_3": 73 }
+{ "a_0": 74, "a_1": 74, "a_2": 74, "a_3": 74, "b_3": 74 }
+{ "a_0": 75, "a_1": 75, "a_2": 75, "a_3": 75, "b_3": 75 }
+{ "a_0": 76, "a_1": 76, "a_2": 76, "a_3": 76, "b_3": 76 }
+{ "a_0": 77, "a_1": 77, "a_2": 77, "a_3": 77, "b_3": 77 }
+{ "a_0": 78, "a_1": 78, "a_2": 78, "a_3": 78, "b_3": 78 }
+{ "a_0": 79, "a_1": 79, "a_2": 79, "a_3": 79, "b_3": 79 }
+{ "a_0": 80, "a_1": 80, "a_2": 80, "a_3": 80, "b_3": 80 }
+{ "a_0": 81, "a_1": 81, "a_2": 81, "a_3": 81, "b_3": 81 }
+{ "a_0": 82, "a_1": 82, "a_2": 82, "a_3": 82, "b_3": 82 }
+{ "a_0": 83, "a_1": 83, "a_2": 83, "a_3": 83, "b_3": 83 }
+{ "a_0": 84, "a_1": 84, "a_2": 84, "a_3": 84, "b_3": 84 }
+{ "a_0": 85, "a_1": 85, "a_2": 85, "a_3": 85, "b_3": 85 }
+{ "a_0": 86, "a_1": 86, "a_2": 86, "a_3": 86, "b_3": 86 }
+{ "a_0": 87, "a_1": 87, "a_2": 87, "a_3": 87, "b_3": 87 }
+{ "a_0": 88, "a_1": 88, "a_2": 88, "a_3": 88, "b_3": 88 }
+{ "a_0": 89, "a_1": 89, "a_2": 89, "a_3": 89, "b_3": 89 }
+{ "a_0": 90, "a_1": 90, "a_2": 90, "a_3": 90, "b_3": 90 }
+{ "a_0": 91, "a_1": 91, "a_2": 91, "a_3": 91, "b_3": 91 }
+{ "a_0": 92, "a_1": 92, "a_2": 92, "a_3": 92, "b_3": 92 }
+{ "a_0": 93, "a_1": 93, "a_2": 93, "a_3": 93, "b_3": 93 }
+{ "a_0": 94, "a_1": 94, "a_2": 94, "a_3": 94, "b_3": 94 }
+{ "a_0": 95, "a_1": 95, "a_2": 95, "a_3": 95, "b_3": 95 }
+{ "a_0": 96, "a_1": 96, "a_2": 96, "a_3": 96, "b_3": 96 }
+{ "a_0": 97, "a_1": 97, "a_2": 97, "a_3": 97, "b_3": 97 }
+{ "a_0": 98, "a_1": 98, "a_2": 98, "a_3": 98, "b_3": 98 }
+{ "a_0": 99, "a_1": 99, "a_2": 99, "a_3": 99, "b_3": 99 }
+{ "a_0": 100, "a_1": 100, "a_2": 100, "a_3": 100, "b_3": 100 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.4.adm
new file mode 100644
index 0000000..497b339
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.4.adm
@@ -0,0 +1,100 @@
+{ "a_0": 1, "a_1": 1, "a_2": 1, "a_3": 1 }
+{ "a_0": 2, "a_1": 2, "a_2": 2, "a_3": 2 }
+{ "a_0": 3, "a_1": 3, "a_2": 3, "a_3": 3 }
+{ "a_0": 4, "a_1": 4, "a_2": 4, "a_3": 4 }
+{ "a_0": 5, "a_1": 5, "a_2": 5, "a_3": 5 }
+{ "a_0": 6, "a_1": 6, "a_2": 6, "a_3": 6 }
+{ "a_0": 7, "a_1": 7, "a_2": 7, "a_3": 7 }
+{ "a_0": 8, "a_1": 8, "a_2": 8, "a_3": 8 }
+{ "a_0": 9, "a_1": 9, "a_2": 9, "a_3": 9 }
+{ "a_0": 10, "a_1": 10, "a_2": 10, "a_3": 10 }
+{ "a_0": 11, "a_1": 11, "a_2": 11, "a_3": 11 }
+{ "a_0": 12, "a_1": 12, "a_2": 12, "a_3": 12 }
+{ "a_0": 13, "a_1": 13, "a_2": 13, "a_3": 13 }
+{ "a_0": 14, "a_1": 14, "a_2": 14, "a_3": 14 }
+{ "a_0": 15, "a_1": 15, "a_2": 15, "a_3": 15 }
+{ "a_0": 16, "a_1": 16, "a_2": 16, "a_3": 16 }
+{ "a_0": 17, "a_1": 17, "a_2": 17, "a_3": 17 }
+{ "a_0": 18, "a_1": 18, "a_2": 18, "a_3": 18 }
+{ "a_0": 19, "a_1": 19, "a_2": 19, "a_3": 19 }
+{ "a_0": 20, "a_1": 20, "a_2": 20, "a_3": 20 }
+{ "a_0": 21, "a_1": 21, "a_2": 21, "a_3": 21 }
+{ "a_0": 22, "a_1": 22, "a_2": 22, "a_3": 22 }
+{ "a_0": 23, "a_1": 23, "a_2": 23, "a_3": 23 }
+{ "a_0": 24, "a_1": 24, "a_2": 24, "a_3": 24 }
+{ "a_0": 25, "a_1": 25, "a_2": 25, "a_3": 25 }
+{ "a_0": 26, "a_1": 26, "a_2": 26, "a_3": 26 }
+{ "a_0": 27, "a_1": 27, "a_2": 27, "a_3": 27 }
+{ "a_0": 28, "a_1": 28, "a_2": 28, "a_3": 28 }
+{ "a_0": 29, "a_1": 29, "a_2": 29, "a_3": 29 }
+{ "a_0": 30, "a_1": 30, "a_2": 30, "a_3": 30 }
+{ "a_0": 31, "a_1": 31, "a_2": 31, "a_3": 31 }
+{ "a_0": 32, "a_1": 32, "a_2": 32, "a_3": 32 }
+{ "a_0": 33, "a_1": 33, "a_2": 33, "a_3": 33 }
+{ "a_0": 34, "a_1": 34, "a_2": 34, "a_3": 34 }
+{ "a_0": 35, "a_1": 35, "a_2": 35, "a_3": 35 }
+{ "a_0": 36, "a_1": 36, "a_2": 36, "a_3": 36 }
+{ "a_0": 37, "a_1": 37, "a_2": 37, "a_3": 37 }
+{ "a_0": 38, "a_1": 38, "a_2": 38, "a_3": 38 }
+{ "a_0": 39, "a_1": 39, "a_2": 39, "a_3": 39 }
+{ "a_0": 40, "a_1": 40, "a_2": 40, "a_3": 40 }
+{ "a_0": 41, "a_1": 41, "a_2": 41, "a_3": 41 }
+{ "a_0": 42, "a_1": 42, "a_2": 42, "a_3": 42 }
+{ "a_0": 43, "a_1": 43, "a_2": 43, "a_3": 43 }
+{ "a_0": 44, "a_1": 44, "a_2": 44, "a_3": 44 }
+{ "a_0": 45, "a_1": 45, "a_2": 45, "a_3": 45 }
+{ "a_0": 46, "a_1": 46, "a_2": 46, "a_3": 46 }
+{ "a_0": 47, "a_1": 47, "a_2": 47, "a_3": 47 }
+{ "a_0": 48, "a_1": 48, "a_2": 48, "a_3": 48 }
+{ "a_0": 49, "a_1": 49, "a_2": 49, "a_3": 49 }
+{ "a_0": 50, "a_1": 50, "a_2": 50, "a_3": 50 }
+{ "a_0": 51, "a_1": 51, "a_2": 51, "a_3": 51, "b_3": 51 }
+{ "a_0": 52, "a_1": 52, "a_2": 52, "a_3": 52, "b_3": 52 }
+{ "a_0": 53, "a_1": 53, "a_2": 53, "a_3": 53, "b_3": 53 }
+{ "a_0": 54, "a_1": 54, "a_2": 54, "a_3": 54, "b_3": 54 }
+{ "a_0": 55, "a_1": 55, "a_2": 55, "a_3": 55, "b_3": 55 }
+{ "a_0": 56, "a_1": 56, "a_2": 56, "a_3": 56, "b_3": 56 }
+{ "a_0": 57, "a_1": 57, "a_2": 57, "a_3": 57, "b_3": 57 }
+{ "a_0": 58, "a_1": 58, "a_2": 58, "a_3": 58, "b_3": 58 }
+{ "a_0": 59, "a_1": 59, "a_2": 59, "a_3": 59, "b_3": 59 }
+{ "a_0": 60, "a_1": 60, "a_2": 60, "a_3": 60, "b_3": 60 }
+{ "a_0": 61, "a_1": 61, "a_2": 61, "a_3": 61, "b_3": 61 }
+{ "a_0": 62, "a_1": 62, "a_2": 62, "a_3": 62, "b_3": 62 }
+{ "a_0": 63, "a_1": 63, "a_2": 63, "a_3": 63, "b_3": 63 }
+{ "a_0": 64, "a_1": 64, "a_2": 64, "a_3": 64, "b_3": 64 }
+{ "a_0": 65, "a_1": 65, "a_2": 65, "a_3": 65, "b_3": 65 }
+{ "a_0": 66, "a_1": 66, "a_2": 66, "a_3": 66, "b_3": 66 }
+{ "a_0": 67, "a_1": 67, "a_2": 67, "a_3": 67, "b_3": 67 }
+{ "a_0": 68, "a_1": 68, "a_2": 68, "a_3": 68, "b_3": 68 }
+{ "a_0": 69, "a_1": 69, "a_2": 69, "a_3": 69, "b_3": 69 }
+{ "a_0": 70, "a_1": 70, "a_2": 70, "a_3": 70, "b_3": 70 }
+{ "a_0": 71, "a_1": 71, "a_2": 71, "a_3": 71, "b_3": 71 }
+{ "a_0": 72, "a_1": 72, "a_2": 72, "a_3": 72, "b_3": 72 }
+{ "a_0": 73, "a_1": 73, "a_2": 73, "a_3": 73, "b_3": 73 }
+{ "a_0": 74, "a_1": 74, "a_2": 74, "a_3": 74, "b_3": 74 }
+{ "a_0": 75, "a_1": 75, "a_2": 75, "a_3": 75, "b_3": 75 }
+{ "a_0": 76, "a_1": 76, "a_2": 76, "a_3": 76, "b_3": 76 }
+{ "a_0": 77, "a_1": 77, "a_2": 77, "a_3": 77, "b_3": 77 }
+{ "a_0": 78, "a_1": 78, "a_2": 78, "a_3": 78, "b_3": 78 }
+{ "a_0": 79, "a_1": 79, "a_2": 79, "a_3": 79, "b_3": 79 }
+{ "a_0": 80, "a_1": 80, "a_2": 80, "a_3": 80, "b_3": 80 }
+{ "a_0": 81, "a_1": 81, "a_2": 81, "a_3": 81, "b_3": 81 }
+{ "a_0": 82, "a_1": 82, "a_2": 82, "a_3": 82, "b_3": 82 }
+{ "a_0": 83, "a_1": 83, "a_2": 83, "a_3": 83, "b_3": 83 }
+{ "a_0": 84, "a_1": 84, "a_2": 84, "a_3": 84, "b_3": 84 }
+{ "a_0": 85, "a_1": 85, "a_2": 85, "a_3": 85, "b_3": 85 }
+{ "a_0": 86, "a_1": 86, "a_2": 86, "a_3": 86, "b_3": 86 }
+{ "a_0": 87, "a_1": 87, "a_2": 87, "a_3": 87, "b_3": 87 }
+{ "a_0": 88, "a_1": 88, "a_2": 88, "a_3": 88, "b_3": 88 }
+{ "a_0": 89, "a_1": 89, "a_2": 89, "a_3": 89, "b_3": 89 }
+{ "a_0": 90, "a_1": 90, "a_2": 90, "a_3": 90, "b_3": 90 }
+{ "a_0": 91, "a_1": 91, "a_2": 91, "a_3": 91, "b_3": 91 }
+{ "a_0": 92, "a_1": 92, "a_2": 92, "a_3": 92, "b_3": 92 }
+{ "a_0": 93, "a_1": 93, "a_2": 93, "a_3": 93, "b_3": 93 }
+{ "a_0": 94, "a_1": 94, "a_2": 94, "a_3": 94, "b_3": 94 }
+{ "a_0": 95, "a_1": 95, "a_2": 95, "a_3": 95, "b_3": 95 }
+{ "a_0": 96, "a_1": 96, "a_2": 96, "a_3": 96, "b_3": 96 }
+{ "a_0": 97, "a_1": 97, "a_2": 97, "a_3": 97, "b_3": 97 }
+{ "a_0": 98, "a_1": 98, "a_2": 98, "a_3": 98, "b_3": 98 }
+{ "a_0": 99, "a_1": 99, "a_2": 99, "a_3": 99, "b_3": 99 }
+{ "a_0": 100, "a_1": 100, "a_2": 100, "a_3": 100, "b_3": 100 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.6.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.6.adm
new file mode 100644
index 0000000..497b339
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.6.adm
@@ -0,0 +1,100 @@
+{ "a_0": 1, "a_1": 1, "a_2": 1, "a_3": 1 }
+{ "a_0": 2, "a_1": 2, "a_2": 2, "a_3": 2 }
+{ "a_0": 3, "a_1": 3, "a_2": 3, "a_3": 3 }
+{ "a_0": 4, "a_1": 4, "a_2": 4, "a_3": 4 }
+{ "a_0": 5, "a_1": 5, "a_2": 5, "a_3": 5 }
+{ "a_0": 6, "a_1": 6, "a_2": 6, "a_3": 6 }
+{ "a_0": 7, "a_1": 7, "a_2": 7, "a_3": 7 }
+{ "a_0": 8, "a_1": 8, "a_2": 8, "a_3": 8 }
+{ "a_0": 9, "a_1": 9, "a_2": 9, "a_3": 9 }
+{ "a_0": 10, "a_1": 10, "a_2": 10, "a_3": 10 }
+{ "a_0": 11, "a_1": 11, "a_2": 11, "a_3": 11 }
+{ "a_0": 12, "a_1": 12, "a_2": 12, "a_3": 12 }
+{ "a_0": 13, "a_1": 13, "a_2": 13, "a_3": 13 }
+{ "a_0": 14, "a_1": 14, "a_2": 14, "a_3": 14 }
+{ "a_0": 15, "a_1": 15, "a_2": 15, "a_3": 15 }
+{ "a_0": 16, "a_1": 16, "a_2": 16, "a_3": 16 }
+{ "a_0": 17, "a_1": 17, "a_2": 17, "a_3": 17 }
+{ "a_0": 18, "a_1": 18, "a_2": 18, "a_3": 18 }
+{ "a_0": 19, "a_1": 19, "a_2": 19, "a_3": 19 }
+{ "a_0": 20, "a_1": 20, "a_2": 20, "a_3": 20 }
+{ "a_0": 21, "a_1": 21, "a_2": 21, "a_3": 21 }
+{ "a_0": 22, "a_1": 22, "a_2": 22, "a_3": 22 }
+{ "a_0": 23, "a_1": 23, "a_2": 23, "a_3": 23 }
+{ "a_0": 24, "a_1": 24, "a_2": 24, "a_3": 24 }
+{ "a_0": 25, "a_1": 25, "a_2": 25, "a_3": 25 }
+{ "a_0": 26, "a_1": 26, "a_2": 26, "a_3": 26 }
+{ "a_0": 27, "a_1": 27, "a_2": 27, "a_3": 27 }
+{ "a_0": 28, "a_1": 28, "a_2": 28, "a_3": 28 }
+{ "a_0": 29, "a_1": 29, "a_2": 29, "a_3": 29 }
+{ "a_0": 30, "a_1": 30, "a_2": 30, "a_3": 30 }
+{ "a_0": 31, "a_1": 31, "a_2": 31, "a_3": 31 }
+{ "a_0": 32, "a_1": 32, "a_2": 32, "a_3": 32 }
+{ "a_0": 33, "a_1": 33, "a_2": 33, "a_3": 33 }
+{ "a_0": 34, "a_1": 34, "a_2": 34, "a_3": 34 }
+{ "a_0": 35, "a_1": 35, "a_2": 35, "a_3": 35 }
+{ "a_0": 36, "a_1": 36, "a_2": 36, "a_3": 36 }
+{ "a_0": 37, "a_1": 37, "a_2": 37, "a_3": 37 }
+{ "a_0": 38, "a_1": 38, "a_2": 38, "a_3": 38 }
+{ "a_0": 39, "a_1": 39, "a_2": 39, "a_3": 39 }
+{ "a_0": 40, "a_1": 40, "a_2": 40, "a_3": 40 }
+{ "a_0": 41, "a_1": 41, "a_2": 41, "a_3": 41 }
+{ "a_0": 42, "a_1": 42, "a_2": 42, "a_3": 42 }
+{ "a_0": 43, "a_1": 43, "a_2": 43, "a_3": 43 }
+{ "a_0": 44, "a_1": 44, "a_2": 44, "a_3": 44 }
+{ "a_0": 45, "a_1": 45, "a_2": 45, "a_3": 45 }
+{ "a_0": 46, "a_1": 46, "a_2": 46, "a_3": 46 }
+{ "a_0": 47, "a_1": 47, "a_2": 47, "a_3": 47 }
+{ "a_0": 48, "a_1": 48, "a_2": 48, "a_3": 48 }
+{ "a_0": 49, "a_1": 49, "a_2": 49, "a_3": 49 }
+{ "a_0": 50, "a_1": 50, "a_2": 50, "a_3": 50 }
+{ "a_0": 51, "a_1": 51, "a_2": 51, "a_3": 51, "b_3": 51 }
+{ "a_0": 52, "a_1": 52, "a_2": 52, "a_3": 52, "b_3": 52 }
+{ "a_0": 53, "a_1": 53, "a_2": 53, "a_3": 53, "b_3": 53 }
+{ "a_0": 54, "a_1": 54, "a_2": 54, "a_3": 54, "b_3": 54 }
+{ "a_0": 55, "a_1": 55, "a_2": 55, "a_3": 55, "b_3": 55 }
+{ "a_0": 56, "a_1": 56, "a_2": 56, "a_3": 56, "b_3": 56 }
+{ "a_0": 57, "a_1": 57, "a_2": 57, "a_3": 57, "b_3": 57 }
+{ "a_0": 58, "a_1": 58, "a_2": 58, "a_3": 58, "b_3": 58 }
+{ "a_0": 59, "a_1": 59, "a_2": 59, "a_3": 59, "b_3": 59 }
+{ "a_0": 60, "a_1": 60, "a_2": 60, "a_3": 60, "b_3": 60 }
+{ "a_0": 61, "a_1": 61, "a_2": 61, "a_3": 61, "b_3": 61 }
+{ "a_0": 62, "a_1": 62, "a_2": 62, "a_3": 62, "b_3": 62 }
+{ "a_0": 63, "a_1": 63, "a_2": 63, "a_3": 63, "b_3": 63 }
+{ "a_0": 64, "a_1": 64, "a_2": 64, "a_3": 64, "b_3": 64 }
+{ "a_0": 65, "a_1": 65, "a_2": 65, "a_3": 65, "b_3": 65 }
+{ "a_0": 66, "a_1": 66, "a_2": 66, "a_3": 66, "b_3": 66 }
+{ "a_0": 67, "a_1": 67, "a_2": 67, "a_3": 67, "b_3": 67 }
+{ "a_0": 68, "a_1": 68, "a_2": 68, "a_3": 68, "b_3": 68 }
+{ "a_0": 69, "a_1": 69, "a_2": 69, "a_3": 69, "b_3": 69 }
+{ "a_0": 70, "a_1": 70, "a_2": 70, "a_3": 70, "b_3": 70 }
+{ "a_0": 71, "a_1": 71, "a_2": 71, "a_3": 71, "b_3": 71 }
+{ "a_0": 72, "a_1": 72, "a_2": 72, "a_3": 72, "b_3": 72 }
+{ "a_0": 73, "a_1": 73, "a_2": 73, "a_3": 73, "b_3": 73 }
+{ "a_0": 74, "a_1": 74, "a_2": 74, "a_3": 74, "b_3": 74 }
+{ "a_0": 75, "a_1": 75, "a_2": 75, "a_3": 75, "b_3": 75 }
+{ "a_0": 76, "a_1": 76, "a_2": 76, "a_3": 76, "b_3": 76 }
+{ "a_0": 77, "a_1": 77, "a_2": 77, "a_3": 77, "b_3": 77 }
+{ "a_0": 78, "a_1": 78, "a_2": 78, "a_3": 78, "b_3": 78 }
+{ "a_0": 79, "a_1": 79, "a_2": 79, "a_3": 79, "b_3": 79 }
+{ "a_0": 80, "a_1": 80, "a_2": 80, "a_3": 80, "b_3": 80 }
+{ "a_0": 81, "a_1": 81, "a_2": 81, "a_3": 81, "b_3": 81 }
+{ "a_0": 82, "a_1": 82, "a_2": 82, "a_3": 82, "b_3": 82 }
+{ "a_0": 83, "a_1": 83, "a_2": 83, "a_3": 83, "b_3": 83 }
+{ "a_0": 84, "a_1": 84, "a_2": 84, "a_3": 84, "b_3": 84 }
+{ "a_0": 85, "a_1": 85, "a_2": 85, "a_3": 85, "b_3": 85 }
+{ "a_0": 86, "a_1": 86, "a_2": 86, "a_3": 86, "b_3": 86 }
+{ "a_0": 87, "a_1": 87, "a_2": 87, "a_3": 87, "b_3": 87 }
+{ "a_0": 88, "a_1": 88, "a_2": 88, "a_3": 88, "b_3": 88 }
+{ "a_0": 89, "a_1": 89, "a_2": 89, "a_3": 89, "b_3": 89 }
+{ "a_0": 90, "a_1": 90, "a_2": 90, "a_3": 90, "b_3": 90 }
+{ "a_0": 91, "a_1": 91, "a_2": 91, "a_3": 91, "b_3": 91 }
+{ "a_0": 92, "a_1": 92, "a_2": 92, "a_3": 92, "b_3": 92 }
+{ "a_0": 93, "a_1": 93, "a_2": 93, "a_3": 93, "b_3": 93 }
+{ "a_0": 94, "a_1": 94, "a_2": 94, "a_3": 94, "b_3": 94 }
+{ "a_0": 95, "a_1": 95, "a_2": 95, "a_3": 95, "b_3": 95 }
+{ "a_0": 96, "a_1": 96, "a_2": 96, "a_3": 96, "b_3": 96 }
+{ "a_0": 97, "a_1": 97, "a_2": 97, "a_3": 97, "b_3": 97 }
+{ "a_0": 98, "a_1": 98, "a_2": 98, "a_3": 98, "b_3": 98 }
+{ "a_0": 99, "a_1": 99, "a_2": 99, "a_3": 99, "b_3": 99 }
+{ "a_0": 100, "a_1": 100, "a_2": 100, "a_3": 100, "b_3": 100 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.8.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.8.adm
new file mode 100644
index 0000000..497b339
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/join/hash_join_exchange_1/hash_join_exchange_1.8.adm
@@ -0,0 +1,100 @@
+{ "a_0": 1, "a_1": 1, "a_2": 1, "a_3": 1 }
+{ "a_0": 2, "a_1": 2, "a_2": 2, "a_3": 2 }
+{ "a_0": 3, "a_1": 3, "a_2": 3, "a_3": 3 }
+{ "a_0": 4, "a_1": 4, "a_2": 4, "a_3": 4 }
+{ "a_0": 5, "a_1": 5, "a_2": 5, "a_3": 5 }
+{ "a_0": 6, "a_1": 6, "a_2": 6, "a_3": 6 }
+{ "a_0": 7, "a_1": 7, "a_2": 7, "a_3": 7 }
+{ "a_0": 8, "a_1": 8, "a_2": 8, "a_3": 8 }
+{ "a_0": 9, "a_1": 9, "a_2": 9, "a_3": 9 }
+{ "a_0": 10, "a_1": 10, "a_2": 10, "a_3": 10 }
+{ "a_0": 11, "a_1": 11, "a_2": 11, "a_3": 11 }
+{ "a_0": 12, "a_1": 12, "a_2": 12, "a_3": 12 }
+{ "a_0": 13, "a_1": 13, "a_2": 13, "a_3": 13 }
+{ "a_0": 14, "a_1": 14, "a_2": 14, "a_3": 14 }
+{ "a_0": 15, "a_1": 15, "a_2": 15, "a_3": 15 }
+{ "a_0": 16, "a_1": 16, "a_2": 16, "a_3": 16 }
+{ "a_0": 17, "a_1": 17, "a_2": 17, "a_3": 17 }
+{ "a_0": 18, "a_1": 18, "a_2": 18, "a_3": 18 }
+{ "a_0": 19, "a_1": 19, "a_2": 19, "a_3": 19 }
+{ "a_0": 20, "a_1": 20, "a_2": 20, "a_3": 20 }
+{ "a_0": 21, "a_1": 21, "a_2": 21, "a_3": 21 }
+{ "a_0": 22, "a_1": 22, "a_2": 22, "a_3": 22 }
+{ "a_0": 23, "a_1": 23, "a_2": 23, "a_3": 23 }
+{ "a_0": 24, "a_1": 24, "a_2": 24, "a_3": 24 }
+{ "a_0": 25, "a_1": 25, "a_2": 25, "a_3": 25 }
+{ "a_0": 26, "a_1": 26, "a_2": 26, "a_3": 26 }
+{ "a_0": 27, "a_1": 27, "a_2": 27, "a_3": 27 }
+{ "a_0": 28, "a_1": 28, "a_2": 28, "a_3": 28 }
+{ "a_0": 29, "a_1": 29, "a_2": 29, "a_3": 29 }
+{ "a_0": 30, "a_1": 30, "a_2": 30, "a_3": 30 }
+{ "a_0": 31, "a_1": 31, "a_2": 31, "a_3": 31 }
+{ "a_0": 32, "a_1": 32, "a_2": 32, "a_3": 32 }
+{ "a_0": 33, "a_1": 33, "a_2": 33, "a_3": 33 }
+{ "a_0": 34, "a_1": 34, "a_2": 34, "a_3": 34 }
+{ "a_0": 35, "a_1": 35, "a_2": 35, "a_3": 35 }
+{ "a_0": 36, "a_1": 36, "a_2": 36, "a_3": 36 }
+{ "a_0": 37, "a_1": 37, "a_2": 37, "a_3": 37 }
+{ "a_0": 38, "a_1": 38, "a_2": 38, "a_3": 38 }
+{ "a_0": 39, "a_1": 39, "a_2": 39, "a_3": 39 }
+{ "a_0": 40, "a_1": 40, "a_2": 40, "a_3": 40 }
+{ "a_0": 41, "a_1": 41, "a_2": 41, "a_3": 41 }
+{ "a_0": 42, "a_1": 42, "a_2": 42, "a_3": 42 }
+{ "a_0": 43, "a_1": 43, "a_2": 43, "a_3": 43 }
+{ "a_0": 44, "a_1": 44, "a_2": 44, "a_3": 44 }
+{ "a_0": 45, "a_1": 45, "a_2": 45, "a_3": 45 }
+{ "a_0": 46, "a_1": 46, "a_2": 46, "a_3": 46 }
+{ "a_0": 47, "a_1": 47, "a_2": 47, "a_3": 47 }
+{ "a_0": 48, "a_1": 48, "a_2": 48, "a_3": 48 }
+{ "a_0": 49, "a_1": 49, "a_2": 49, "a_3": 49 }
+{ "a_0": 50, "a_1": 50, "a_2": 50, "a_3": 50 }
+{ "a_0": 51, "a_1": 51, "a_2": 51, "a_3": 51, "b_3": 51 }
+{ "a_0": 52, "a_1": 52, "a_2": 52, "a_3": 52, "b_3": 52 }
+{ "a_0": 53, "a_1": 53, "a_2": 53, "a_3": 53, "b_3": 53 }
+{ "a_0": 54, "a_1": 54, "a_2": 54, "a_3": 54, "b_3": 54 }
+{ "a_0": 55, "a_1": 55, "a_2": 55, "a_3": 55, "b_3": 55 }
+{ "a_0": 56, "a_1": 56, "a_2": 56, "a_3": 56, "b_3": 56 }
+{ "a_0": 57, "a_1": 57, "a_2": 57, "a_3": 57, "b_3": 57 }
+{ "a_0": 58, "a_1": 58, "a_2": 58, "a_3": 58, "b_3": 58 }
+{ "a_0": 59, "a_1": 59, "a_2": 59, "a_3": 59, "b_3": 59 }
+{ "a_0": 60, "a_1": 60, "a_2": 60, "a_3": 60, "b_3": 60 }
+{ "a_0": 61, "a_1": 61, "a_2": 61, "a_3": 61, "b_3": 61 }
+{ "a_0": 62, "a_1": 62, "a_2": 62, "a_3": 62, "b_3": 62 }
+{ "a_0": 63, "a_1": 63, "a_2": 63, "a_3": 63, "b_3": 63 }
+{ "a_0": 64, "a_1": 64, "a_2": 64, "a_3": 64, "b_3": 64 }
+{ "a_0": 65, "a_1": 65, "a_2": 65, "a_3": 65, "b_3": 65 }
+{ "a_0": 66, "a_1": 66, "a_2": 66, "a_3": 66, "b_3": 66 }
+{ "a_0": 67, "a_1": 67, "a_2": 67, "a_3": 67, "b_3": 67 }
+{ "a_0": 68, "a_1": 68, "a_2": 68, "a_3": 68, "b_3": 68 }
+{ "a_0": 69, "a_1": 69, "a_2": 69, "a_3": 69, "b_3": 69 }
+{ "a_0": 70, "a_1": 70, "a_2": 70, "a_3": 70, "b_3": 70 }
+{ "a_0": 71, "a_1": 71, "a_2": 71, "a_3": 71, "b_3": 71 }
+{ "a_0": 72, "a_1": 72, "a_2": 72, "a_3": 72, "b_3": 72 }
+{ "a_0": 73, "a_1": 73, "a_2": 73, "a_3": 73, "b_3": 73 }
+{ "a_0": 74, "a_1": 74, "a_2": 74, "a_3": 74, "b_3": 74 }
+{ "a_0": 75, "a_1": 75, "a_2": 75, "a_3": 75, "b_3": 75 }
+{ "a_0": 76, "a_1": 76, "a_2": 76, "a_3": 76, "b_3": 76 }
+{ "a_0": 77, "a_1": 77, "a_2": 77, "a_3": 77, "b_3": 77 }
+{ "a_0": 78, "a_1": 78, "a_2": 78, "a_3": 78, "b_3": 78 }
+{ "a_0": 79, "a_1": 79, "a_2": 79, "a_3": 79, "b_3": 79 }
+{ "a_0": 80, "a_1": 80, "a_2": 80, "a_3": 80, "b_3": 80 }
+{ "a_0": 81, "a_1": 81, "a_2": 81, "a_3": 81, "b_3": 81 }
+{ "a_0": 82, "a_1": 82, "a_2": 82, "a_3": 82, "b_3": 82 }
+{ "a_0": 83, "a_1": 83, "a_2": 83, "a_3": 83, "b_3": 83 }
+{ "a_0": 84, "a_1": 84, "a_2": 84, "a_3": 84, "b_3": 84 }
+{ "a_0": 85, "a_1": 85, "a_2": 85, "a_3": 85, "b_3": 85 }
+{ "a_0": 86, "a_1": 86, "a_2": 86, "a_3": 86, "b_3": 86 }
+{ "a_0": 87, "a_1": 87, "a_2": 87, "a_3": 87, "b_3": 87 }
+{ "a_0": 88, "a_1": 88, "a_2": 88, "a_3": 88, "b_3": 88 }
+{ "a_0": 89, "a_1": 89, "a_2": 89, "a_3": 89, "b_3": 89 }
+{ "a_0": 90, "a_1": 90, "a_2": 90, "a_3": 90, "b_3": 90 }
+{ "a_0": 91, "a_1": 91, "a_2": 91, "a_3": 91, "b_3": 91 }
+{ "a_0": 92, "a_1": 92, "a_2": 92, "a_3": 92, "b_3": 92 }
+{ "a_0": 93, "a_1": 93, "a_2": 93, "a_3": 93, "b_3": 93 }
+{ "a_0": 94, "a_1": 94, "a_2": 94, "a_3": 94, "b_3": 94 }
+{ "a_0": 95, "a_1": 95, "a_2": 95, "a_3": 95, "b_3": 95 }
+{ "a_0": 96, "a_1": 96, "a_2": 96, "a_3": 96, "b_3": 96 }
+{ "a_0": 97, "a_1": 97, "a_2": 97, "a_3": 97, "b_3": 97 }
+{ "a_0": 98, "a_1": 98, "a_2": 98, "a_3": 98, "b_3": 98 }
+{ "a_0": 99, "a_1": 99, "a_2": 99, "a_3": 99, "b_3": 99 }
+{ "a_0": 100, "a_1": 100, "a_2": 100, "a_3": 100, "b_3": 100 }
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
index 2701f15..15a94fe 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
@@ -6660,6 +6660,16 @@
       </compilation-unit>
     </test-case>
     <test-case FilePath="join">
+      <compilation-unit name="hash_join_exchange">
+        <output-dir compare="Text">hash_join_exchange</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="join">
+      <compilation-unit name="hash_join_exchange_1">
+        <output-dir compare="Text">hash_join_exchange_1</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="join">
       <compilation-unit name="ASTERIXDB-3502">
         <output-dir compare="Text">ASTERIXDB-3502</output-dir>
       </compilation-unit>
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractHashJoinPOperator.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractHashJoinPOperator.java
index c9f59c6..443b211 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractHashJoinPOperator.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/AbstractHashJoinPOperator.java
@@ -137,24 +137,25 @@
                                 .getPartitioningType() == requirements.getPartitioningType()) {
                             switch (requirements.getPartitioningType()) {
                                 case UNORDERED_PARTITIONED: {
-                                    UnorderedPartitionedProperty upp1 =
+                                    UnorderedPartitionedProperty unorderedFirstDelivered =
                                             (UnorderedPartitionedProperty) firstDeliveredPartitioning;
-                                    Set<LogicalVariable> set1 = upp1.getColumnSet();
-                                    UnorderedPartitionedProperty uppreq = (UnorderedPartitionedProperty) requirements;
-                                    Set<LogicalVariable> modifuppreq = new ListSet<LogicalVariable>();
+                                    Set<LogicalVariable> firstDeliveredVars = unorderedFirstDelivered.getColumnSet();
+                                    UnorderedPartitionedProperty unorderedRequired =
+                                            (UnorderedPartitionedProperty) requirements;
+                                    Set<LogicalVariable> modifiedRequiredVars = new ListSet<LogicalVariable>();
                                     Map<LogicalVariable, EquivalenceClass> eqmap = context.getEquivalenceClassMap(op);
-                                    Set<LogicalVariable> covered = new ListSet<LogicalVariable>();
-                                    Set<LogicalVariable> keysCurrent = uppreq.getColumnSet();
-                                    List<LogicalVariable> keysFirst = (keysRightBranch.containsAll(keysCurrent))
+                                    Set<LogicalVariable> coveredVars = new ListSet<LogicalVariable>();
+                                    List<LogicalVariable> keysFirst = (keysRightBranch.containsAll(firstDeliveredVars))
                                             ? keysRightBranch : keysLeftBranch;
                                     List<LogicalVariable> keysSecond =
                                             keysFirst == keysRightBranch ? keysLeftBranch : keysRightBranch;
-                                    for (LogicalVariable r : uppreq.getColumnSet()) {
-                                        EquivalenceClass ecSnd = eqmap.get(r);
+                                    for (LogicalVariable r : firstDeliveredVars) {
+                                        EquivalenceClass ecFirst = eqmap.get(r);
                                         boolean found = false;
                                         int j = 0;
                                         for (LogicalVariable rvar : keysFirst) {
-                                            if (rvar == r || ecSnd != null && eqmap.get(rvar) == ecSnd) {
+                                            if (!modifiedRequiredVars.contains(keysSecond.get(j))
+                                                    && (rvar == r || (ecFirst != null && ecFirst == eqmap.get(rvar)))) {
                                                 found = true;
                                                 break;
                                             }
@@ -165,27 +166,19 @@
                                                     + " among " + keysFirst);
                                         }
                                         LogicalVariable v2 = keysSecond.get(j);
-                                        EquivalenceClass ecFst = eqmap.get(v2);
-                                        for (LogicalVariable vset1 : set1) {
-                                            if (vset1 == v2 || ecFst != null && eqmap.get(vset1) == ecFst) {
-                                                if (!covered.add(vset1)) {
-                                                    continue;
-                                                }
-                                                modifuppreq.add(r);
-                                                break;
-                                            }
-                                        }
-                                        if (covered.equals(set1)) {
+                                        coveredVars.add(r);
+                                        modifiedRequiredVars.add(v2);
+                                        if (coveredVars.equals(firstDeliveredVars)) {
                                             break;
                                         }
                                     }
-                                    if (!covered.equals(set1)) {
+                                    if (!coveredVars.equals(firstDeliveredVars)) {
                                         throw new AlgebricksException("Could not modify " + requirements
                                                 + " to agree with partitioning property " + firstDeliveredPartitioning
                                                 + " delivered by previous input operator.");
                                     }
-                                    UnorderedPartitionedProperty upp2 =
-                                            new UnorderedPartitionedProperty(modifuppreq, requirements.getNodeDomain());
+                                    UnorderedPartitionedProperty upp2 = new UnorderedPartitionedProperty(
+                                            modifiedRequiredVars, requirements.getNodeDomain());
                                     return new Pair<>(false, upp2);
                                 }
                                 case ORDERED_PARTITIONED: {