public void rowUpdated(
        final Rule rule,
        final LeftTuple resultLeftTuple,
        final PropagationContext context,
        final InternalWorkingMemory workingMemory) {
      RightTuple rightTuple = (RightTuple) resultLeftTuple.getObject();
      if (rightTuple.getMemory() != null) {
        // Already sheduled as an insert
        return;
      }

      rightTuple.setLeftTuple(null);
      resultLeftTuple.setObject(null);

      // We need to recopy everything back again, as we don't know what has or hasn't changed
      QueryTerminalNode node = (QueryTerminalNode) resultLeftTuple.getLeftTupleSink();
      Declaration[] decls = node.getDeclarations();
      InternalFactHandle rootHandle = resultLeftTuple.get(0);
      DroolsQuery query = (DroolsQuery) rootHandle.getObject();

      Object[] objects = new Object[query.getElements().length];

      Declaration decl;
      for (int i = 0, length = this.variables.length; i < length; i++) {
        decl = decls[this.variables[i]];
        objects[this.variables[i]] =
            decl.getValue(workingMemory, resultLeftTuple.get(decl).getObject());
      }

      QueryElementFactHandle handle = (QueryElementFactHandle) rightTuple.getFactHandle();

      handle.setRecency(workingMemory.getFactHandleFactory().getAtomicRecency().incrementAndGet());
      handle.setObject(objects);

      if (query.isOpen()) {
        rightTuple.setLeftTuple(resultLeftTuple);
        resultLeftTuple.setObject(rightTuple);
      }

      // Don't need to recreate child links, as they will already be there form the first "add"

      RightTupleList rightTuples = query.getResultUpdateRightTupleList();
      if (rightTuples == null) {
        rightTuples = new RightTupleList();
        query.setResultUpdateRightTupleList(rightTuples);
        QueryResultUpdateAction updateAction =
            new QueryResultUpdateAction(context, this.factHandle, leftTuple, this.node);
        context.getQueue2().addFirst(updateAction);
      }
      rightTuples.add(rightTuple);
    }
    public void rowAdded(
        final Rule rule,
        LeftTuple resultLeftTuple,
        PropagationContext context,
        InternalWorkingMemory workingMemory) {

      QueryTerminalNode node = (QueryTerminalNode) resultLeftTuple.getLeftTupleSink();
      Declaration[] decls = node.getDeclarations();
      DroolsQuery query = (DroolsQuery) this.factHandle.getObject();
      Object[] objects = new Object[query.getElements().length];

      Declaration decl;
      for (int i = 0, length = this.variables.length; i < length; i++) {
        decl = decls[this.variables[i]];
        objects[this.variables[i]] =
            decl.getValue(workingMemory, resultLeftTuple.get(decl).getObject());
      }

      QueryElementFactHandle resultHandle =
          createQueryResultHandle(context, workingMemory, objects);

      RightTuple rightTuple = new RightTuple(resultHandle);
      if (query.isOpen()) {
        rightTuple.setLeftTuple(resultLeftTuple);
        resultLeftTuple.setObject(rightTuple);
      }

      this.node
          .getSinkPropagator()
          .createChildLeftTuplesforQuery(
              this.leftTuple,
              rightTuple,
              true, // this must always be true, otherwise we can't
              // find the child tuples to iterate for evaluating the query results
              query.isOpen());

      RightTupleList rightTuples = query.getResultInsertRightTupleList();
      if (rightTuples == null) {
        rightTuples = new RightTupleList();
        query.setResultInsertRightTupleList(rightTuples);
        QueryResultInsertAction evalAction =
            new QueryResultInsertAction(context, this.factHandle, leftTuple, this.node);
        context.getQueue2().addFirst(evalAction);
      }

      rightTuples.add(rightTuple);
    }
  public void testQueryTerminalNode() {
    final ClassObjectType queryObjectType = new ClassObjectType(DroolsQuery.class);
    final ObjectTypeNode queryObjectTypeNode =
        new ObjectTypeNode(this.buildContext.getNextId(), queryObjectType, buildContext);
    queryObjectTypeNode.attach();

    ClassFieldExtractor extractor =
        cache.getExtractor(DroolsQuery.class, "name", DroolsQuery.class.getClassLoader());

    FieldValue field = FieldFactory.getFieldValue("query-1");

    final Evaluator evaluator = ValueType.STRING_TYPE.getEvaluator(Operator.EQUAL);
    LiteralConstraint constraint = new LiteralConstraint(extractor, evaluator, field);

    AlphaNode alphaNode =
        new AlphaNode(this.buildContext.getNextId(), constraint, queryObjectTypeNode, buildContext);
    alphaNode.attach();

    final LeftInputAdapterNode liaNode =
        new LeftInputAdapterNode(this.buildContext.getNextId(), alphaNode, this.buildContext);
    liaNode.attach();

    final ClassObjectType cheeseObjectType = new ClassObjectType(Cheese.class);
    final ObjectTypeNode cheeseObjectTypeNode =
        new ObjectTypeNode(this.buildContext.getNextId(), cheeseObjectType, buildContext);
    cheeseObjectTypeNode.attach();

    extractor = cache.getExtractor(Cheese.class, "type", getClass().getClassLoader());

    field = FieldFactory.getFieldValue("stilton");

    constraint = new LiteralConstraint(extractor, evaluator, field);

    alphaNode =
        new AlphaNode(
            this.buildContext.getNextId(), constraint, cheeseObjectTypeNode, buildContext);
    alphaNode.attach();

    BuildContext buildContext =
        new BuildContext(ruleBase, ruleBase.getReteooBuilder().getIdGenerator());
    buildContext.setTupleMemoryEnabled(false);

    final JoinNode joinNode =
        new JoinNode(
            this.buildContext.getNextId(),
            liaNode,
            alphaNode,
            EmptyBetaConstraints.getInstance(),
            buildContext);
    joinNode.attach();

    final Query query = new Query("query-1");

    final QueryTerminalNode queryNode =
        new QueryTerminalNode(this.buildContext.getNextId(), joinNode, query, query.getLhs());

    queryNode.attach();

    final org.drools.rule.Package pkg = new org.drools.rule.Package("com.drools.test");
    pkg.addRule(query);

    try {
      final Field pkgField = ruleBase.getClass().getSuperclass().getDeclaredField("pkgs");
      pkgField.setAccessible(true);
      final Map pkgs = (Map) pkgField.get(ruleBase);
      pkgs.put(pkg.getName(), pkg);
    } catch (final Exception e) {
      Assert.fail("Should not throw any exception: " + e.getMessage());
    }

    final WorkingMemory workingMemory = ruleBase.newStatefulSession();
    QueryResults results = workingMemory.getQueryResults("query-1");

    assertEquals(0, results.size());

    final Cheese stilton1 = new Cheese("stilton", 100);
    final FactHandle handle1 = workingMemory.insert(stilton1);

    results = workingMemory.getQueryResults("query-1");

    assertEquals(1, results.size());

    final Cheese cheddar = new Cheese("cheddar", 55);
    workingMemory.insert(cheddar);

    results = workingMemory.getQueryResults("query-1");

    assertEquals(1, results.size());

    final Cheese stilton2 = new Cheese("stilton", 5);

    final FactHandle handle2 = workingMemory.insert(stilton2);

    results = workingMemory.getQueryResults("query-1");

    assertEquals(2, results.size());

    QueryResult result = results.get(0);
    assertEquals(1, result.size());
    assertEquals(stilton2, result.get(0));

    result = results.get(1);
    assertEquals(1, result.size());
    assertEquals(stilton1, result.get(0));

    int i = 0;
    for (final Iterator it = results.iterator(); it.hasNext(); ) {
      result = (QueryResult) it.next();
      assertEquals(1, result.size());
      if (i == 1) {
        assertSame(stilton1, result.get(0));
      } else {
        assertSame(stilton2, result.get(0));
      }
      i++;
    }

    workingMemory.retract(handle1);
    results = workingMemory.getQueryResults("query-1");

    assertEquals(1, results.size());

    workingMemory.retract(handle2);
    results = workingMemory.getQueryResults("query-1");

    assertEquals(0, results.size());
  }