public IndexNestedLoopJoinOperatorNodePushable(
     AbstractTreeIndexOperatorDescriptor opDesc,
     IHyracksTaskContext ctx,
     int partition,
     IRecordDescriptorProvider recordDescProvider,
     boolean isForward,
     int[] lowKeyFields,
     int[] highKeyFields,
     boolean lowKeyInclusive,
     boolean highKeyInclusive) {
   inputRecDesc = recordDescProvider.getInputRecordDescriptor(opDesc.getActivityId(), 0);
   treeIndexOpHelper =
       (IndexDataflowHelper)
           opDesc
               .getIndexDataflowHelperFactory()
               .createIndexDataflowHelper(opDesc, ctx, partition);
   this.lowKeyInclusive = lowKeyInclusive;
   this.highKeyInclusive = highKeyInclusive;
   this.recDesc = recordDescProvider.getInputRecordDescriptor(opDesc.getActivityId(), 0);
   if (lowKeyFields != null && lowKeyFields.length > 0) {
     lowKey = new PermutingFrameTupleReference();
     lowKey.setFieldPermutation(lowKeyFields);
   }
   if (highKeyFields != null && highKeyFields.length > 0) {
     highKey = new PermutingFrameTupleReference();
     highKey.setFieldPermutation(highKeyFields);
   }
 }
 @Override
 public void nextFrame(ByteBuffer buffer) throws HyracksDataException {
   accessor.reset(buffer);
   int tupleCount = accessor.getTupleCount();
   try {
     for (int i = 0; i < tupleCount; ) {
       if (lowKey != null) lowKey.reset(accessor, i);
       if (highKey != null) highKey.reset(accessor, i);
       // TODO: currently use low key only, check what they mean
       if (currentTopTuple != null) {
         int cmp = compare(lowKey, currentTopTuple);
         if (cmp == 0) {
           outputMatch(i);
           i++;
         } else if ((cmp > 0)) {
           moveTreeCursor();
         } else {
           writeLeftResults(accessor, i);
           i++;
         }
       } else {
         writeLeftResults(accessor, i);
         i++;
       }
     }
   } catch (Exception e) {
     throw new HyracksDataException(e);
   }
 }
  @Override
  public void open() throws HyracksDataException {
    accessor = new FrameTupleAccessor(treeIndexOpHelper.getTaskContext().getFrameSize(), recDesc);

    try {
      treeIndexOpHelper.open();
      index = (ITreeIndex) treeIndexOpHelper.getIndexInstance();
      writer.open();

      int lowKeySearchFields = index.getComparatorFactories().length;
      int highKeySearchFields = index.getComparatorFactories().length;
      if (lowKey != null) lowKeySearchFields = lowKey.getFieldCount();
      if (highKey != null) highKeySearchFields = highKey.getFieldCount();

      IBinaryComparator[] lowKeySearchComparators = new IBinaryComparator[lowKeySearchFields];
      for (int i = 0; i < lowKeySearchFields; i++) {
        lowKeySearchComparators[i] = index.getComparatorFactories()[i].createBinaryComparator();
      }
      lowKeySearchCmp = new MultiComparator(lowKeySearchComparators);

      if (lowKeySearchFields == highKeySearchFields) {
        highKeySearchCmp = lowKeySearchCmp;
      } else {
        IBinaryComparator[] highKeySearchComparators = new IBinaryComparator[highKeySearchFields];
        for (int i = 0; i < highKeySearchFields; i++) {
          highKeySearchComparators[i] = index.getComparatorFactories()[i].createBinaryComparator();
        }
        highKeySearchCmp = new MultiComparator(highKeySearchComparators);
      }

      rangePred =
          new RangePredicate(
              null, null, lowKeyInclusive, highKeyInclusive, lowKeySearchCmp, highKeySearchCmp);
      writeBuffer = treeIndexOpHelper.getTaskContext().allocateFrame();
      tb = new ArrayTupleBuilder(inputRecDesc.getFields().length + index.getFieldCount());
      dos = tb.getDataOutput();
      appender = new FrameTupleAppender(treeIndexOpHelper.getTaskContext().getFrameSize());
      appender.reset(writeBuffer, true);
      indexAccessor =
          index.createAccessor(NoOpOperationCallback.INSTANCE, NoOpOperationCallback.INSTANCE);
      setCursor();
    } catch (Exception e) {
      treeIndexOpHelper.close();
      throw new HyracksDataException(e);
    }
  }
  @Override
  public void nextFrame(ByteBuffer buffer) throws HyracksDataException {
    accessor.reset(buffer);

    int tupleCount = accessor.getTupleCount();
    try {
      for (int i = 0; i < tupleCount; i++) {
        if (lowKey != null) lowKey.reset(accessor, i);
        if (highKey != null) highKey.reset(accessor, i);
        rangePred.setLowKey(lowKey, lowKeyInclusive);
        rangePred.setHighKey(highKey, highKeyInclusive);

        cursor.reset();
        indexAccessor.search(cursor, rangePred);
        writeSearchResults(accessor, i);
      }
    } catch (Exception e) {
      throw new HyracksDataException(e);
    }
  }
  public BTreeSearchFunctionUpdateOperatorNodePushable(
      AbstractTreeIndexOperatorDescriptor opDesc,
      IHyracksTaskContext ctx,
      int partition,
      IRecordDescriptorProvider recordDescProvider,
      boolean isForward,
      int[] lowKeyFields,
      int[] highKeyFields,
      boolean lowKeyInclusive,
      boolean highKeyInclusive,
      IUpdateFunctionFactory functionFactory,
      IRuntimeHookFactory preHookFactory,
      IRuntimeHookFactory postHookFactory,
      IRecordDescriptorFactory inputRdFactory,
      int outputArity) {
    treeIndexHelper =
        (TreeIndexDataflowHelper)
            opDesc
                .getIndexDataflowHelperFactory()
                .createIndexDataflowHelper(opDesc, ctx, partition);
    this.isForward = isForward;
    this.lowKeyInclusive = lowKeyInclusive;
    this.highKeyInclusive = highKeyInclusive;
    this.recDesc = recordDescProvider.getInputRecordDescriptor(opDesc.getActivityId(), 0);
    if (lowKeyFields != null && lowKeyFields.length > 0) {
      lowKey = new PermutingFrameTupleReference();
      lowKey.setFieldPermutation(lowKeyFields);
    }
    if (highKeyFields != null && highKeyFields.length > 0) {
      highKey = new PermutingFrameTupleReference();
      highKey.setFieldPermutation(highKeyFields);
    }

    this.writers = new IFrameWriter[outputArity];
    this.functionProxy =
        new FunctionProxy(
            ctx, functionFactory, preHookFactory, postHookFactory, inputRdFactory, writers);
    this.updateBuffer = new UpdateBuffer(ctx, 2);
  }