@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));
  }
  /**
   * Parse with expectation of success.
   *
   * @param query The query or update request.
   * @throws MalformedQueryException
   */
  private void parseOperation(final String query) throws MalformedQueryException {

    if (useBigdataParser) {

      new Bigdata2ASTSPARQLParser(tripleStore).parseOperation(query, baseURI);

    } else {

      QueryParserUtil.parseOperation(QueryLanguage.SPARQL, query, baseURI);
    }
  }
  @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();
    }
  }
  /**
   * {@inheritDoc}
   *
   * <p>This uses the {@link Bigdata2ASTSPARQLParser}.
   *
   * @return
   */
  @Override
  protected ParsedOperation parseOperation(final String query, final String queryFileURL)
      throws MalformedQueryException {

    try {

      if (useBigdataParser) {
        // bigdata parser.
        return new Bigdata2ASTSPARQLParser(tripleStore).parseOperation(query, queryFileURL);

      } else {
        // openrdf parser.
        return QueryParserUtil.parseOperation(QueryLanguage.SPARQL, query, queryFileURL);
      }

    } catch (MalformedQueryException ex) {

      throw new MalformedQueryException(
          ex + ": query=" + query + ", queryFileURL=" + queryFileURL, ex);
    }
  }
  static {
    try {
      DIRECT_SUBCLASSOF_MATCHER =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL, "CONSTRUCT * FROM {X} sesame:directSubClassOf {Y} ", null);

      DIRECT_SUBPROPERTYOF_MATCHER =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL, "CONSTRUCT * FROM {X} sesame:directType {Y}", null);

      DIRECT_TYPE_MATCHER =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL, "CONSTRUCT * FROM {X} sesame:directSubPropertyOf {Y}", null);

      DIRECT_SUBCLASSOF_QUERY =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL,
              "CONSTRUCT {X} sesame:directSubClassOf {Y} "
                  + "FROM {X} rdfs:subClassOf {Y} "
                  + "WHERE X != Y AND "
                  + "NOT EXISTS (SELECT Z FROM {X} rdfs:subClassOf {Z} rdfs:subClassOf {Y} WHERE X != Z AND Z != Y)",
              null);

      DIRECT_SUBPROPERTYOF_QUERY =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL,
              "CONSTRUCT {X} sesame:directSubPropertyOf {Y} "
                  + "FROM {X} rdfs:subPropertyOf {Y} "
                  + "WHERE X != Y AND "
                  + "NOT EXISTS (SELECT Z FROM {X} rdfs:subPropertyOf {Z} rdfs:subPropertyOf {Y} WHERE X != Z AND Z != Y)",
              null);

      DIRECT_TYPE_QUERY =
          QueryParserUtil.parseGraphQuery(
              QueryLanguage.SERQL,
              "CONSTRUCT {X} sesame:directType {Y} FROM {X} rdf:type {Y} "
                  + "WHERE NOT EXISTS (SELECT Z FROM {X} rdf:type {Z} rdfs:subClassOf {Y} WHERE Z != Y)",
              null);
    } catch (MalformedQueryException e) {
      // Can only occur due to a bug in this code
      throw new RuntimeException(e);
    }
  }