@Test
  public void testQueryBindings() throws Exception {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter, context1);
    con.addStatement(guernica, RDF.TYPE, painting, context1);
    con.addStatement(picasso, paints, guernica, context1);
    con.commit();

    // Query 1
    ParsedTupleQuery tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL, "select X from {X} rdf:type {Y} rdf:type {rdfs:Class}", null);
    TupleExpr tupleExpr = tupleQuery.getTupleExpr();

    MapBindingSet bindings = new MapBindingSet(2);
    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;

    iter = con.evaluate(tupleExpr, null, bindings, false);
    int resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 2, resultCount);

    bindings.addBinding("Y", painter);
    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);

    bindings.addBinding("Z", painting);
    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);

    bindings.removeBinding("Y");
    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 2, resultCount);

    // Query 2
    tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL,
            "select X from {X} rdf:type {Y} rdf:type {rdfs:Class} where Y = Z",
            null);
    tupleExpr = tupleQuery.getTupleExpr();
    bindings.clear();

    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 0, resultCount);

    bindings.addBinding("Z", painter);
    iter = con.evaluate(tupleExpr, null, bindings, false);
    resultCount = verifyQueryResult(iter, 1);
    assertEquals("Wrong number of query results", 1, resultCount);
  }
  @Test
  public void testAddWhileQuerying() throws Exception {
    // Add some data to the repository
    con.begin();
    con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
    con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
    con.addStatement(picasso, RDF.TYPE, painter);
    con.addStatement(guernica, RDF.TYPE, painting);
    con.addStatement(picasso, paints, guernica);
    con.commit();

    ParsedTupleQuery tupleQuery =
        QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, "SELECT C FROM {} rdf:type {C}", null);

    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
    iter = con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false);

    con.begin();

    while (iter.hasNext()) {
      BindingSet bindings = iter.next();
      Value c = bindings.getValue("C");
      if (c instanceof Resource) {
        con.addStatement((Resource) c, RDF.TYPE, RDFS.CLASS);
      }
    }

    con.commit();

    assertEquals(3, countElements(con.getStatements(null, RDF.TYPE, RDFS.CLASS, false)));

    // simulate auto-commit
    tupleQuery =
        QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, "SELECT P FROM {} P {}", null);
    iter = con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false);

    while (iter.hasNext()) {
      BindingSet bindings = iter.next();
      Value p = bindings.getValue("P");
      if (p instanceof URI) {
        con.begin();
        con.addStatement((URI) p, RDF.TYPE, RDF.PROPERTY);
        con.commit();
      }
    }

    assertEquals(2, countElements(con.getStatements(null, RDF.TYPE, RDF.PROPERTY, false)));
  }
  protected int countQueryResults(String query) throws Exception {
    ParsedTupleQuery tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL, query + " using namespace ex = <" + EXAMPLE_NS + ">", null);

    return countElements(
        con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false));
  }
  @Test
  public void testDualConnections() throws Exception {
    SailConnection con2 = sail.getConnection();
    try {
      assertEquals(0, countAllElements());
      con.begin();
      con.addStatement(painter, RDF.TYPE, RDFS.CLASS);
      con.addStatement(painting, RDF.TYPE, RDFS.CLASS);
      con.addStatement(picasso, RDF.TYPE, painter, context1);
      con.addStatement(guernica, RDF.TYPE, painting, context1);
      con.commit();
      assertEquals(4, countAllElements());
      con2.begin();
      con2.addStatement(RDF.NIL, RDF.TYPE, RDF.LIST);
      String query = "SELECT S, P, O FROM {S} P {O}";
      ParsedTupleQuery tupleQuery =
          QueryParserUtil.parseTupleQuery(QueryLanguage.SERQL, query, null);
      assertEquals(
          5,
          countElements(
              con2.evaluate(
                  tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false)));
      Runnable clearer =
          new Runnable() {

            public void run() {
              try {
                con.begin();
                con.clear();
                con.commit();
              } catch (SailException e) {
                throw new RuntimeException(e);
              }
            }
          };
      Thread thread = new Thread(clearer);
      thread.start();
      Thread.yield();
      Thread.yield();
      con2.commit();
      thread.join();
    } finally {
      con2.close();
    }
  }
  protected void testValueRoundTrip(Resource subj, URI pred, Value obj) throws Exception {
    con.begin();
    con.addStatement(subj, pred, obj);
    con.commit();

    CloseableIteration<? extends Statement, SailException> stIter =
        con.getStatements(null, null, null, false);

    try {
      assertTrue(stIter.hasNext());

      Statement st = stIter.next();
      assertEquals(subj, st.getSubject());
      assertEquals(pred, st.getPredicate());
      assertEquals(obj, st.getObject());
      assertTrue(!stIter.hasNext());
    } finally {
      stIter.close();
    }

    ParsedTupleQuery tupleQuery =
        QueryParserUtil.parseTupleQuery(
            QueryLanguage.SERQL,
            "SELECT S, P, O FROM {S} P {O} WHERE P = <" + pred.stringValue() + ">",
            null);

    CloseableIteration<? extends BindingSet, QueryEvaluationException> iter;
    iter = con.evaluate(tupleQuery.getTupleExpr(), null, EmptyBindingSet.getInstance(), false);

    try {
      assertTrue(iter.hasNext());

      BindingSet bindings = iter.next();
      assertEquals(subj, bindings.getValue("S"));
      assertEquals(pred, bindings.getValue("P"));
      assertEquals(obj, bindings.getValue("O"));
      assertTrue(!iter.hasNext());
    } finally {
      iter.close();
    }
  }