docs: private void generateInnerAccessPaths(BranchNode parentNode) {
          JoinNode innerChildNode = parentNode.getRightNode();
          assert(innerChildNode != null);
          // In case of inner join WHERE and JOIN expressions can be merged
          if (parentNode.getJoinType() == JoinType.INNER) {
              parentNode.m_joinInnerOuterList.addAll(parentNode.m_whereInnerOuterList);
              parentNode.m_whereInnerOuterList.clear();
              parentNode.m_joinInnerList.addAll(parentNode.m_whereInnerList);
              parentNode.m_whereInnerList.clear();
          }
          if (innerChildNode instanceof BranchNode) {
              generateOuterAccessPaths((BranchNode)innerChildNode);
              generateInnerAccessPaths((BranchNode)innerChildNode);
              // The inner node is a join node itself. Only naive access path is possible
              innerChildNode.m_accessPaths.add(
                      getRelevantNaivePath(parentNode.m_joinInnerOuterList, parentNode.m_joinInnerList));
              return;
          }
  
          // The inner table can have multiple index access paths based on
          // inner and inner-outer join expressions plus the naive one.
          List<AbstractExpression> filterExprs = null;
          List<AbstractExpression> postExprs = null;
          // For the FULL join type, the inner join expressions must stay at the join node and
          // not go down to the inner node as filters (as predicates for SeqScan nodes and/or
          // index expressions for Index Scan). The latter case (IndexScan) won't work for NLJ because
          // the inner join expression will effectively filter out inner tuple prior to the NLJ.
          if (parentNode.getJoinType() != JoinType.FULL) {
              filterExprs = parentNode.m_joinInnerList;
          }
          else {
              postExprs = parentNode.m_joinInnerList;
          }
          StmtTableScan innerTable = innerChildNode.getTableScan();
          assert(innerTable != null);
          innerChildNode.m_accessPaths.addAll(
                  getRelevantAccessPathsForTable(innerTable,
                          parentNode.m_joinInnerOuterList, filterExprs, postExprs));
  
          // If there are inner expressions AND inner-outer expressions, it could be that there
          // are indexed access paths that use elements of both in the indexing expressions,
          // especially in the case of a compound index.
          // These access paths can not be considered for use with an NLJ because they rely on
          // inner-outer expressions.
          // If there is a possibility that NLIJ will not be an option due to the
          // "special case" processing that puts a send/receive plan between the join node
          // and its inner child node, other access paths need to be considered that use the
          // same indexes as those identified so far but in a simpler, less effective way
          // that does not rely on inner-outer expressions.
          // The following simplistic method of finding these access paths is to force
          // inner-outer expressions to be handled as NLJ-compatible post-filters and repeat
          // the search for access paths.
          // This will typically generate some duplicate access paths, including the naive
          // sequential scan path and any indexed paths that happened to use only the inner
          // expressions.
          // For now, we deal with this redundancy by dropping (and re-generating) all
          // access paths EXCPT those that reference the inner-outer expressions.
          // TODO: implementing access path hash and equality and possibly using a "Set"
          // would allow deduping as new access paths are added OR
          // the simplified access path search process could be based on
          // the existing indexed access paths -- for each access path that "hasInnerOuterIndexExpression"
          // try to generate and add a simpler access path using the same index,
          // this time with the inner-outer expressions used only as non-indexable post-filters.
  
          // Don't bother generating these redundant or inferior access paths unless there is
          // an inner-outer expression and a chance that NLIJ will be taken out of the running.
          boolean mayNeedInnerSendReceive = ( ! m_partitioning.wasSpecifiedAsSingle()) &&
                  (m_partitioning.getCountOfPartitionedTables() > 0) &&
                  (parentNode.getJoinType() != JoinType.INNER) &&
                  ! innerTable.getIsReplicated();
  // too expensive/complicated to test here? (parentNode.m_leftNode has a replicated result?) &&
  
          if (mayNeedInnerSendReceive && ! parentNode.m_joinInnerOuterList.isEmpty()) {
              List<AccessPath> innerOuterAccessPaths = new ArrayList<>();
              for (AccessPath innerAccessPath : innerChildNode.m_accessPaths) {
                  if ((innerAccessPath.index != null) &&
                      hasInnerOuterIndexExpression(innerChildNode.getTableAlias(),
                                                   innerAccessPath.indexExprs,
                                                   innerAccessPath.initialExpr,
                                                   innerAccessPath.endExprs)) {
                      innerOuterAccessPaths.add(innerAccessPath);
                  }
              }
              if (parentNode.getJoinType() != JoinType.FULL) {
                  filterExprs = parentNode.m_joinInnerList;
                  postExprs = parentNode.m_joinInnerOuterList;
              }
              else {
                  // For FULL join type the inner join expressions must be part of the post predicate
                  // in order to stay at the join node and not be pushed down to the inner node
                  filterExprs = null;
                  postExprs = new ArrayList<>(parentNode.m_joinInnerList);
                  postExprs.addAll(parentNode.m_joinInnerOuterList);
              }
              Collection<AccessPath>
              nljAccessPaths = getRelevantAccessPathsForTable(
                      innerTable, null, filterExprs, postExprs);
  
              innerChildNode.m_accessPaths.clear();
              innerChildNode.m_accessPaths.addAll(nljAccessPaths);
              innerChildNode.m_accessPaths.addAll(innerOuterAccessPaths);
          }
  
          assert(innerChildNode.m_accessPaths.size() > 0);
      }