This is an automated email from the ASF dual-hosted git repository.

alsuliman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 271cf79c1c [ASTERIXDB-3231][COMP] Fix the index-only plan of indexnl 
left outer join
271cf79c1c is described below

commit 271cf79c1c1064834ffba77fd62378fc6061bf6b
Author: Ali Alsuliman <[email protected]>
AuthorDate: Wed Aug 2 23:32:10 2023 -0700

    [ASTERIXDB-3231][COMP] Fix the index-only plan of indexnl left outer join
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    Details:
    For SELECT op, include missingPlaceholderVar when substituting
    SELECT op variables.
    
    Change-Id: I07e619e2fbeef0e0679548d9c1b2e108d8bc3128
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17665
    Integration-Tests: Jenkins <[email protected]>
    Tested-by: Jenkins <[email protected]>
    Reviewed-by: Ali Alsuliman <[email protected]>
---
 asterixdb/asterix-app/pom.xml                      |   2 +
 .../runtime/SqlppExecutionFullParallelismIT.java   |   2 +-
 .../runtime/SqlppExecutionLessParallelismIT.java   |   2 +-
 .../index-only-leftouterjoin.001.ddl.sqlpp         |  35 +++++++
 .../index-only-leftouterjoin.002.update.sqlpp      |  42 +++++++++
 .../index-only-leftouterjoin.003.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.004.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.005.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.006.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.007.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.008.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.009.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.010.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.011.query.sqlpp       |  25 +++++
 .../index-only-leftouterjoin.003.adm               |   2 +
 .../index-only-leftouterjoin.004.adm               |   2 +
 .../index-only-leftouterjoin.005.adm               |   2 +
 .../index-only-leftouterjoin.006.adm               |   2 +
 .../index-only-leftouterjoin.007.plan              |  62 +++++++++++++
 .../index-only-leftouterjoin.008.plan              |  94 +++++++++++++++++++
 .../index-only-leftouterjoin.009.plan              |  62 +++++++++++++
 .../index-only-leftouterjoin.010.plan              |  94 +++++++++++++++++++
 .../index-only-leftouterjoin.011.adm               |   2 +
 .../index-only-leftouterjoin.007.plan              |  62 +++++++++++++
 .../index-only-leftouterjoin.008.plan              |  94 +++++++++++++++++++
 .../index-only-leftouterjoin.009.plan              |  62 +++++++++++++
 .../index-only-leftouterjoin.010.plan              |  94 +++++++++++++++++++
 .../index-only-leftouterjoin.008.plan              | 102 +++++++++++++++++++++
 .../index-only-leftouterjoin.010.plan              | 102 +++++++++++++++++++++
 .../index-only-leftouterjoin.008.plan              | 102 +++++++++++++++++++++
 .../index-only-leftouterjoin.010.plan              | 102 +++++++++++++++++++++
 .../test/resources/runtimets/testsuite_sqlpp.xml   |   5 +
 .../algebra/operators/logical/SelectOperator.java  |  21 +++--
 .../visitors/SubstituteVariableVisitor.java        |   4 +
 34 files changed, 1371 insertions(+), 9 deletions(-)

diff --git a/asterixdb/asterix-app/pom.xml b/asterixdb/asterix-app/pom.xml
index a0ff91a8ad..ef74df4f12 100644
--- a/asterixdb/asterix-app/pom.xml
+++ b/asterixdb/asterix-app/pom.xml
@@ -267,6 +267,8 @@
                 
<exclude>src/test/resources/**/results_parser_sqlpp/**</exclude>
                 <exclude>src/test/resources/**/results/**</exclude>
                 <exclude>src/test/resources/**/results_cbo/**</exclude>
+                
<exclude>src/test/resources/**/results_less_parallelism/**</exclude>
+                
<exclude>src/test/resources/**/results_full_parallelism/**</exclude>
                 
<exclude>src/test/resources/fuzzyjoin/pub/fuzzy-join-aql*.dot</exclude>
                 
<exclude>src/test/resources/fuzzyjoin/pub/fuzzy-join-aql*.json</exclude>
                 <exclude>**/data/**</exclude>
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionFullParallelismIT.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionFullParallelismIT.java
index df7976b6a3..5d3eac0b03 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionFullParallelismIT.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionFullParallelismIT.java
@@ -39,7 +39,7 @@ public class SqlppExecutionFullParallelismIT {
 
     @BeforeClass
     public static void setUp() throws Exception {
-        LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, new TestExecutor());
+        LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, new 
TestExecutor("results_full_parallelism"));
     }
 
     @AfterClass
diff --git 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionLessParallelismIT.java
 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionLessParallelismIT.java
index d99590d0ad..dd84bef9ea 100644
--- 
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionLessParallelismIT.java
+++ 
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/runtime/SqlppExecutionLessParallelismIT.java
@@ -39,7 +39,7 @@ public class SqlppExecutionLessParallelismIT {
 
     @BeforeClass
     public static void setUp() throws Exception {
-        LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, new TestExecutor());
+        LangExecutionUtil.setUp(TEST_CONFIG_FILE_NAME, new 
TestExecutor("results_less_parallelism"));
     }
 
     @AfterClass
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.001.ddl.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.001.ddl.sqlpp
new file mode 100644
index 0000000000..4ae9ae82fa
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.001.ddl.sqlpp
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+
+DROP DATAVERSE test IF EXISTS;
+CREATE DATAVERSE test;
+USE test;
+
+CREATE TYPE untyped AS {id: string};
+CREATE TYPE typed AS {id: string, c_int32: int32?};
+
+CREATE DATASET ds_outer_untyped(untyped) primary key id;
+CREATE DATASET ds_outer_typed(typed) primary key id;
+
+CREATE DATASET ds_inner_untyped(untyped) primary key id;
+CREATE DATASET ds_inner_typed(typed) primary key id;
+
+CREATE INDEX idx_c_int32 ON ds_inner_untyped(c_int32: int32);
+CREATE INDEX idx_c_int32 ON ds_inner_typed(c_int32);
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.002.update.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.002.update.sqlpp
new file mode 100644
index 0000000000..6d916a9c93
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.002.update.sqlpp
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+UPSERT INTO ds_outer_untyped [
+{'id': "o_untyped:01", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{'id': "o_untyped:02", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null}
+];
+
+UPSERT INTO ds_outer_typed [
+{'id': "o_untyped:01", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{'id': "o_untyped:02", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null}
+];
+
+UPSERT INTO ds_inner_untyped [
+{ 'id': "i_untyped:01", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{ 'id': "i_untyped:02", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{ 'id': "i_untyped:03", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null}
+];
+
+UPSERT INTO ds_inner_typed [
+{ 'id': "i_typed:01", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{ 'id': "i_typed:02", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null},
+{ 'id': "i_typed:03", 'c_any':null, 'c_int8':null, 'c_int16':null, 
'c_int32':null, 'c_int64':null, 'c_float':null, 'c_double':null}
+];
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.query.sqlpp
new file mode 100644
index 0000000000..27a4019b87
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_untyped t1 LEFT JOIN ds_inner_untyped t2 ON int32(t1.c_int32) /* 
+indexnl */ =(t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.query.sqlpp
new file mode 100644
index 0000000000..6bff38deb0
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_untyped t1 LEFT JOIN ds_inner_typed t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.query.sqlpp
new file mode 100644
index 0000000000..1ac4c6b1f7
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_typed t1 LEFT JOIN ds_inner_untyped t2 ON int32(t1.c_int32) /* 
+indexnl */ =(t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.query.sqlpp
new file mode 100644
index 0000000000..6e0740fa67
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_typed t1 LEFT JOIN ds_inner_typed t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.query.sqlpp
new file mode 100644
index 0000000000..b78ca994df
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+EXPLAIN SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_untyped t1 LEFT JOIN ds_inner_untyped t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.query.sqlpp
new file mode 100644
index 0000000000..9ca5ca6502
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+EXPLAIN SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_untyped t1 LEFT JOIN ds_inner_typed t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.query.sqlpp
new file mode 100644
index 0000000000..c3fefcb3ce
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+EXPLAIN SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_typed t1 LEFT JOIN ds_inner_untyped t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.query.sqlpp
new file mode 100644
index 0000000000..0c793929ca
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'true';
+EXPLAIN SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_typed t1 LEFT JOIN ds_inner_typed t2 ON int32(t1.c_int32) /* 
+indexnl */ = (t2.c_int32) ORDER BY t1_id, t2_id;
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.query.sqlpp
 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.query.sqlpp
new file mode 100644
index 0000000000..c66ae86c58
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.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.
+ */
+
+USE test;
+
+SET `compiler.sort.parallel` 'false';
+SET `compiler.indexonly` 'false';
+SELECT t1.id AS t1_id, t2.id AS t2_id
+FROM ds_outer_untyped t1 LEFT JOIN ds_inner_untyped t2 ON int32(t1.c_int32) = 
(t2.c_int32) ORDER BY t1_id, t2_id;
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.adm
new file mode 100644
index 0000000000..169686078d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.003.adm
@@ -0,0 +1,2 @@
+{ "t1_id": "o_untyped:01" }
+{ "t1_id": "o_untyped:02" }
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.adm
new file mode 100644
index 0000000000..169686078d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.004.adm
@@ -0,0 +1,2 @@
+{ "t1_id": "o_untyped:01" }
+{ "t1_id": "o_untyped:02" }
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.adm
new file mode 100644
index 0000000000..169686078d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.005.adm
@@ -0,0 +1,2 @@
+{ "t1_id": "o_untyped:01" }
+{ "t1_id": "o_untyped:02" }
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.adm
new file mode 100644
index 0000000000..169686078d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.006.adm
@@ -0,0 +1,2 @@
+{ "t1_id": "o_untyped:01" }
+{ "t1_id": "o_untyped:02" }
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
new file mode 100644
index 0000000000..f4cf2d4a5c
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
@@ -0,0 +1,62 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$53) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$53, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$62) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$62] <- 
[win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$53) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$53(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$53]  |PARTITIONED|
+                          project ([$$53, $$54]) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            select (eq($$55, $$t2.getField("c_int32"))) 
retain-untrue ($$54 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                            -- STREAM_SELECT  |PARTITIONED|
+                              project ([$$53, $$55, $$54, $$t2]) [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-unnest-map [$$54, $$t2] <- 
index-search("ds_inner_untyped", 0, "test", "ds_inner_untyped", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                  -- BTREE_SEARCH  |PARTITIONED|
+                                    exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      order (ASC, $$61) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                      -- STABLE_SORT [$$61(ASC)]  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          project ([$$53, $$55, $$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-unnest-map [$$60, 
$$61] <- index-search("idx_c_int32", 0, "test", "ds_inner_untyped", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_untyped [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/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
new file mode 100644
index 0000000000..b1db9f84c2
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
@@ -0,0 +1,94 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- BTREE_SEARCH  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        project ([$$53, $$55, $$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|
+                                            split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- SPLIT  |PARTITIONED|
+                                              exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                              -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                                -- BTREE_SEARCH  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                    project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                    -- STREAM_PROJECT  
|PARTITIONED|
+                                                      assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_untyped [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, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$60)) retain-untrue ($$61 <- 
missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  project ([$$53, $$55, $$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|
+                                      split ($$62) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- SPLIT  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          left-outer-unnest-map [$$60, $$61, 
$$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                          -- BTREE_SEARCH  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                              project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 []<-[$$53, $$t1] 
<- test.ds_outer_untyped [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/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
new file mode 100644
index 0000000000..591375e4cf
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
@@ -0,0 +1,62 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$53) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$53, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$62) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$62] <- 
[win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$53) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$53(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$53]  |PARTITIONED|
+                          project ([$$53, $$54]) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            select (eq($$55, $$t2.getField("c_int32"))) 
retain-untrue ($$54 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                            -- STREAM_SELECT  |PARTITIONED|
+                              project ([$$53, $$55, $$54, $$t2]) [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-unnest-map [$$54, $$t2] <- 
index-search("ds_inner_untyped", 0, "test", "ds_inner_untyped", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                  -- BTREE_SEARCH  |PARTITIONED|
+                                    exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      order (ASC, $$61) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                      -- STABLE_SORT [$$61(ASC)]  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          project ([$$53, $$55, $$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-unnest-map [$$60, 
$$61] <- index-search("idx_c_int32", 0, "test", "ds_inner_untyped", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField(1))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_typed [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/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
new file mode 100644
index 0000000000..3d31e11b5b
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
@@ -0,0 +1,94 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- BTREE_SEARCH  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        project ([$$53, $$55, $$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|
+                                            split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- SPLIT  |PARTITIONED|
+                                              exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                              -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                                -- BTREE_SEARCH  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                    project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                    -- STREAM_PROJECT  
|PARTITIONED|
+                                                      assign [$$55] <- 
[int32($$t1.getField(1))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_typed [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, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$60)) retain-untrue ($$61 <- 
missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  project ([$$53, $$55, $$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|
+                                      split ($$62) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- SPLIT  |PARTITIONED|
+                                        exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          left-outer-unnest-map [$$60, $$61, 
$$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                          -- BTREE_SEARCH  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                              project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                assign [$$55] <- 
[int32($$t1.getField(1))] [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 []<-[$$53, $$t1] 
<- test.ds_outer_typed [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/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.adm
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.adm
new file mode 100644
index 0000000000..169686078d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.011.adm
@@ -0,0 +1,2 @@
+{ "t1_id": "o_untyped:01" }
+{ "t1_id": "o_untyped:02" }
\ No newline at end of file
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
new file mode 100644
index 0000000000..a4bd07bd41
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.007.plan
@@ -0,0 +1,62 @@
+distribute result [$$52] [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$52]) [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] [cardinality: 3.15, 
op-cost: 0.0, total-cost: 12.6]
+      -- ASSIGN  |PARTITIONED|
+        exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+        -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$53) (ASC, $$54) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+          -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$53, $$54]) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$62) [cardinality: 3.15, op-cost: 2.1, total-cost: 
12.6]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$62] <- 
[win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$53) (DESC, $$54) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                      -- STABLE_SORT [$$53(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                        -- HASH_PARTITION_EXCHANGE [$$53]  |PARTITIONED|
+                          project ([$$53, $$54]) [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            select (eq($$55, $$t2.getField("c_int32"))) 
retain-untrue ($$54 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                            -- STREAM_SELECT  |PARTITIONED|
+                              project ([$$53, $$55, $$54, $$t2]) [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  left-outer-unnest-map [$$54, $$t2] <- 
index-search("ds_inner_untyped", 0, "test", "ds_inner_untyped", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 3.0, op-cost: 3.0, total-cost: 
3.0]
+                                  -- BTREE_SEARCH  |PARTITIONED|
+                                    exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      order (ASC, $$61) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                      -- STABLE_SORT [$$61(ASC)]  |PARTITIONED|
+                                        exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          project ([$$53, $$55, $$61]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61] <- index-search("idx_c_int32", 0, "test", "ds_inner_untyped", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                                                    -- ASSIGN  |PARTITIONED|
+                                                      exchange [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                                                      -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                        data-scan []<-[$$53, 
$$t1] <- test.ds_outer_untyped [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                        -- 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/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
new file mode 100644
index 0000000000..21db7f65da
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
@@ -0,0 +1,94 @@
+distribute result [$$52] [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$52]) [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [cardinality: 3.15, 
op-cost: 0.0, total-cost: 12.6]
+      -- ASSIGN  |PARTITIONED|
+        exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 3.15, op-cost: 2.1, total-cost: 
12.6]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$53]) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- BTREE_SEARCH  |PARTITIONED|
+                                      exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        project ([$$53, $$55, $$61]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                        -- STREAM_PROJECT  |PARTITIONED|
+                                          exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            split ($$62) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- SPLIT  |PARTITIONED|
+                                              exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                              -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                                -- BTREE_SEARCH  |PARTITIONED|
+                                                  exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                  -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                    project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                    -- STREAM_PROJECT  
|PARTITIONED|
+                                                      assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                                                      -- ASSIGN  |PARTITIONED|
+                                                        exchange [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                                                        -- ONE_TO_ONE_EXCHANGE 
 |PARTITIONED|
+                                                          data-scan []<-[$$53, 
$$t1] <- test.ds_outer_untyped [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                          -- 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, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$60)) retain-untrue ($$61 <- 
missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  project ([$$53, $$55, $$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|
+                                      split ($$62) [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                      -- SPLIT  |PARTITIONED|
+                                        exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          left-outer-unnest-map [$$60, $$61, 
$$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                          -- BTREE_SEARCH  |PARTITIONED|
+                                            exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                              project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                                                -- ASSIGN  |PARTITIONED|
+                                                  exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    data-scan []<-[$$53, $$t1] 
<- test.ds_outer_untyped [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                    -- 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/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
new file mode 100644
index 0000000000..33f5c67db4
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.009.plan
@@ -0,0 +1,62 @@
+distribute result [$$52] [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$52]) [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$53, "t2_id": $$54}] [cardinality: 3.15, 
op-cost: 0.0, total-cost: 12.6]
+      -- ASSIGN  |PARTITIONED|
+        exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+        -- SORT_MERGE_EXCHANGE [$$53(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$53) (ASC, $$54) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+          -- STABLE_SORT [$$53(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$53, $$54]) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$62) [cardinality: 3.15, op-cost: 2.1, total-cost: 
12.6]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$62] <- 
[win-mark-first-missing-impl($$54)] partition [$$53] order (DESC, $$54) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$53) (DESC, $$54) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                      -- STABLE_SORT [$$53(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                        -- HASH_PARTITION_EXCHANGE [$$53]  |PARTITIONED|
+                          project ([$$53, $$54]) [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            select (eq($$55, $$t2.getField("c_int32"))) 
retain-untrue ($$54 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                            -- STREAM_SELECT  |PARTITIONED|
+                              project ([$$53, $$55, $$54, $$t2]) [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  left-outer-unnest-map [$$54, $$t2] <- 
index-search("ds_inner_untyped", 0, "test", "ds_inner_untyped", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 3.0, op-cost: 3.0, total-cost: 
3.0]
+                                  -- BTREE_SEARCH  |PARTITIONED|
+                                    exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      order (ASC, $$61) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                      -- STABLE_SORT [$$61(ASC)]  |PARTITIONED|
+                                        exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          project ([$$53, $$55, $$61]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                          -- STREAM_PROJECT  |PARTITIONED|
+                                            exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61] <- index-search("idx_c_int32", 0, "test", "ds_inner_untyped", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField(1))] [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                    -- ASSIGN  |PARTITIONED|
+                                                      exchange [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                                                      -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                        data-scan []<-[$$53, 
$$t1] <- test.ds_outer_typed [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                        -- 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/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
new file mode 100644
index 0000000000..66c4cf3155
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_cbo/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
@@ -0,0 +1,94 @@
+distribute result [$$52] [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+-- DISTRIBUTE_RESULT  |PARTITIONED|
+  exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+    project ([$$52]) [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [cardinality: 3.15, 
op-cost: 0.0, total-cost: 12.6]
+      -- ASSIGN  |PARTITIONED|
+        exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 3.15, op-cost: 0.0, total-cost: 12.6]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 3.15, op-cost: 0.0, 
total-cost: 12.6]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 3.15, op-cost: 2.1, total-cost: 
12.6]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 2.0, op-cost: 0.0, total-cost: 
2.1]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$53]) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  exchange [cardinality: 2.0, op-cost: 0.0, 
total-cost: 2.1]
+                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                    left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- BTREE_SEARCH  |PARTITIONED|
+                                      exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        project ([$$53, $$55, $$61]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                        -- STREAM_PROJECT  |PARTITIONED|
+                                          exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            split ($$62) [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- SPLIT  |PARTITIONED|
+                                              exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                              -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                                -- BTREE_SEARCH  |PARTITIONED|
+                                                  exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                  -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                    project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                    -- STREAM_PROJECT  
|PARTITIONED|
+                                                      assign [$$55] <- 
[int32($$t1.getField(1))] [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                      -- ASSIGN  |PARTITIONED|
+                                                        exchange [cardinality: 
2.0, op-cost: 0.0, total-cost: 2.1]
+                                                        -- ONE_TO_ONE_EXCHANGE 
 |PARTITIONED|
+                                                          data-scan []<-[$$53, 
$$t1] <- test.ds_outer_typed [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                          -- 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, $$53]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                select (eq($$55, $$60)) retain-untrue ($$61 <- 
missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  project ([$$53, $$55, $$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|
+                                      split ($$62) [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                      -- SPLIT  |PARTITIONED|
+                                        exchange [cardinality: 2.0, op-cost: 
0.0, total-cost: 2.1]
+                                        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                          left-outer-unnest-map [$$60, $$61, 
$$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, true, 
1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                          -- BTREE_SEARCH  |PARTITIONED|
+                                            exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                            -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                              project ([$$53, $$55]) 
[cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                              -- STREAM_PROJECT  |PARTITIONED|
+                                                assign [$$55] <- 
[int32($$t1.getField(1))] [cardinality: 2.0, op-cost: 0.0, total-cost: 2.1]
+                                                -- ASSIGN  |PARTITIONED|
+                                                  exchange [cardinality: 2.0, 
op-cost: 0.0, total-cost: 2.1]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    data-scan []<-[$$53, $$t1] 
<- test.ds_outer_typed [cardinality: 2.0, op-cost: 2.1, total-cost: 2.1]
+                                                    -- 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/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
new file mode 100644
index 0000000000..1fa96f44a2
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
@@ -0,0 +1,102 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61, $$70]) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                        -- BTREE_SEARCH  |PARTITIONED|
+                                          exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            project ([$$53, $$55, $$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|
+                                                split ($$62) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                -- SPLIT  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    left-outer-unnest-map 
[$$60, $$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", 
true, true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                                    -- BTREE_SEARCH  
|PARTITIONED|
+                                                      exchange [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                      -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                        project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                        -- STREAM_PROJECT  
|PARTITIONED|
+                                                          assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 
[]<-[$$53, $$t1] <- test.ds_outer_untyped [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, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$60)) retain-untrue 
($$61 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      project ([$$53, $$55, $$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|
+                                          split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                          -- SPLIT  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_untyped [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/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
new file mode 100644
index 0000000000..493929731d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_full_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
@@ -0,0 +1,102 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61, $$70]) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                        -- BTREE_SEARCH  |PARTITIONED|
+                                          exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            project ([$$53, $$55, $$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|
+                                                split ($$62) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                -- SPLIT  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    left-outer-unnest-map 
[$$60, $$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", 
true, true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                                    -- BTREE_SEARCH  
|PARTITIONED|
+                                                      exchange [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                      -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                        project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                        -- STREAM_PROJECT  
|PARTITIONED|
+                                                          assign [$$55] <- 
[int32($$t1.getField(1))] [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 
[]<-[$$53, $$t1] <- test.ds_outer_typed [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, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$60)) retain-untrue 
($$61 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      project ([$$53, $$55, $$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|
+                                          split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                          -- SPLIT  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField(1))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_typed [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/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
new file mode 100644
index 0000000000..1fa96f44a2
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.008.plan
@@ -0,0 +1,102 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61, $$70]) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                        -- BTREE_SEARCH  |PARTITIONED|
+                                          exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            project ([$$53, $$55, $$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|
+                                                split ($$62) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                -- SPLIT  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    left-outer-unnest-map 
[$$60, $$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", 
true, true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                                    -- BTREE_SEARCH  
|PARTITIONED|
+                                                      exchange [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                      -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                        project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                        -- STREAM_PROJECT  
|PARTITIONED|
+                                                          assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 
[]<-[$$53, $$t1] <- test.ds_outer_untyped [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, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$60)) retain-untrue 
($$61 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      project ([$$53, $$55, $$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|
+                                          split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                          -- SPLIT  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField("c_int32"))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_untyped [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/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
new file mode 100644
index 0000000000..493929731d
--- /dev/null
+++ 
b/asterixdb/asterix-app/src/test/resources/runtimets/results_less_parallelism/leftouterjoin/index-only-leftouterjoin/index-only-leftouterjoin.010.plan
@@ -0,0 +1,102 @@
+distribute result [$$52] [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 ([$$52]) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+    -- STREAM_PROJECT  |PARTITIONED|
+      assign [$$52] <- [{"t1_id": $$73, "t2_id": $$54}] [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]
+        -- SORT_MERGE_EXCHANGE [$$73(ASC), $$54(ASC) ]  |PARTITIONED|
+          order (ASC, $$73) (ASC, $$54) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+          -- STABLE_SORT [$$73(ASC), $$54(ASC)]  |PARTITIONED|
+            exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+              project ([$$73, $$54]) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+              -- STREAM_PROJECT  |PARTITIONED|
+                select ($$74) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                -- STREAM_SELECT  |PARTITIONED|
+                  window-aggregate [$$74] <- 
[win-mark-first-missing-impl($$54)] partition [$$73] order (DESC, $$54) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                  -- WINDOW_STREAM  |PARTITIONED|
+                    exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                      order (ASC, $$73) (DESC, $$54) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                      -- STABLE_SORT [$$73(ASC), $$54(DESC)]  |PARTITIONED|
+                        exchange [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                        -- HASH_PARTITION_EXCHANGE [$$73]  |PARTITIONED|
+                          union ($$70, $$61, $$54) ($$53, $$53, $$73) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                          -- UNION_ALL  |PARTITIONED|
+                            exchange [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                            -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                              project ([$$70, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61, $$70]) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$71.getField(1))) 
retain-untrue ($$70 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        left-outer-unnest-map [$$70, $$71] <- 
index-search("ds_inner_typed", 0, "test", "ds_inner_typed", true, false, 1, 
$$61, 1, $$61, true, true, true) [cardinality: 0.0, op-cost: 0.0, total-cost: 
0.0]
+                                        -- BTREE_SEARCH  |PARTITIONED|
+                                          exchange [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            project ([$$53, $$55, $$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|
+                                                split ($$62) [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                -- SPLIT  |PARTITIONED|
+                                                  exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                  -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                                    left-outer-unnest-map 
[$$60, $$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", 
true, true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 
0.0, total-cost: 0.0]
+                                                    -- BTREE_SEARCH  
|PARTITIONED|
+                                                      exchange [cardinality: 
0.0, op-cost: 0.0, total-cost: 0.0]
+                                                      -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                        project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                        -- STREAM_PROJECT  
|PARTITIONED|
+                                                          assign [$$55] <- 
[int32($$t1.getField(1))] [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 
[]<-[$$53, $$t1] <- test.ds_outer_typed [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, $$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]
+                                -- RANDOM_PARTITION_EXCHANGE  |PARTITIONED|
+                                  project ([$$53, $$61]) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                  -- STREAM_PROJECT  |PARTITIONED|
+                                    select (eq($$55, $$60)) retain-untrue 
($$61 <- missing) [cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                    -- STREAM_SELECT  |PARTITIONED|
+                                      project ([$$53, $$55, $$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|
+                                          split ($$62) [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                          -- SPLIT  |PARTITIONED|
+                                            exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                            -- ONE_TO_ONE_EXCHANGE  
|PARTITIONED|
+                                              left-outer-unnest-map [$$60, 
$$61, $$62] <- index-search("idx_c_int32", 0, "test", "ds_inner_typed", true, 
true, 1, $$55, 1, $$55, true, true, true) [cardinality: 0.0, op-cost: 0.0, 
total-cost: 0.0]
+                                              -- BTREE_SEARCH  |PARTITIONED|
+                                                exchange [cardinality: 0.0, 
op-cost: 0.0, total-cost: 0.0]
+                                                -- BROADCAST_EXCHANGE  
|PARTITIONED|
+                                                  project ([$$53, $$55]) 
[cardinality: 0.0, op-cost: 0.0, total-cost: 0.0]
+                                                  -- STREAM_PROJECT  
|PARTITIONED|
+                                                    assign [$$55] <- 
[int32($$t1.getField(1))] [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 []<-[$$53, 
$$t1] <- test.ds_outer_typed [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/testsuite_sqlpp.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index d24873e3b1..fc0cecad7b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -14300,6 +14300,11 @@
         <output-dir compare="Text">right_branch_opt_1</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="leftouterjoin">
+      <compilation-unit name="index-only-leftouterjoin">
+        <output-dir compare="Text">index-only-leftouterjoin</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="index-leftouterjoin">
     <test-case FilePath="index-leftouterjoin">
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/SelectOperator.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/SelectOperator.java
index 0b2c32d251..3bcf29c00c 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/SelectOperator.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/SelectOperator.java
@@ -46,19 +46,19 @@ import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
 public class SelectOperator extends AbstractLogicalOperator {
     private final Mutable<ILogicalExpression> condition;
     private final IAlgebricksConstantValue retainMissingAsValue;
-    private final LogicalVariable nullPlaceholderVar;
+    private LogicalVariable missingPlaceholderVar;
 
     public SelectOperator(Mutable<ILogicalExpression> condition) {
         this(condition, null, null);
     }
 
     public SelectOperator(Mutable<ILogicalExpression> condition, 
IAlgebricksConstantValue retainMissingAsValue,
-            LogicalVariable nullPlaceholderVar) {
+            LogicalVariable missingPlaceholderVar) {
         this.condition = condition;
         if (retainMissingAsValue == null) {
             this.retainMissingAsValue = null;
-            if (nullPlaceholderVar != null) {
-                throw new 
IllegalArgumentException(nullPlaceholderVar.toString());
+            if (missingPlaceholderVar != null) {
+                throw new 
IllegalArgumentException(missingPlaceholderVar.toString());
             }
         } else if (retainMissingAsValue.isMissing()) {
             this.retainMissingAsValue = ConstantExpression.MISSING.getValue();
@@ -67,7 +67,7 @@ public class SelectOperator extends AbstractLogicalOperator {
         } else {
             throw new 
IllegalArgumentException(retainMissingAsValue.toString());
         }
-        this.nullPlaceholderVar = nullPlaceholderVar;
+        this.missingPlaceholderVar = missingPlaceholderVar;
     }
 
     @Override
@@ -84,12 +84,19 @@ public class SelectOperator extends AbstractLogicalOperator 
{
     }
 
     public LogicalVariable getMissingPlaceholderVariable() {
-        return nullPlaceholderVar;
+        return missingPlaceholderVar;
+    }
+
+    public void setMissingPlaceholderVar(LogicalVariable var) {
+        if (var != null && retainMissingAsValue == null) {
+            throw new IllegalArgumentException("NULL/MISSING var " + var + " 
is set, but its value not specified");
+        }
+        missingPlaceholderVar = var;
     }
 
     @Override
     public void recomputeSchema() {
-        schema = new 
ArrayList<LogicalVariable>(inputs.get(0).getValue().getSchema());
+        schema = new ArrayList<>(inputs.get(0).getValue().getSchema());
     }
 
     @Override
diff --git 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
index 8349945d4a..faf3c1141e 100644
--- 
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
+++ 
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
@@ -257,6 +257,10 @@ public class SubstituteVariableVisitor
     public Void visitSelectOperator(SelectOperator op, Pair<LogicalVariable, 
LogicalVariable> pair)
             throws AlgebricksException {
         substUsedVariablesInExpr(op.getCondition(), pair.first, pair.second);
+        LogicalVariable missingPlaceholderVar = 
op.getMissingPlaceholderVariable();
+        if (missingPlaceholderVar != null && 
missingPlaceholderVar.equals(pair.first)) {
+            op.setMissingPlaceholderVar(pair.second);
+        }
         // SELECT operator may add its used variable
         // to its own output type environment as 'nonMissableVariable' 
(not(is-missing($used_var))
         // therefore we need perform variable substitution in its own type 
environment

Reply via email to