[Mulgara-svn] r1969 - in trunk: . conf lib src/jar/tuples-hybrid/java/org/mulgara/store src/jar/tuples-hybrid/java/org/mulgara/store/bdb

pag at mulgara.org pag at mulgara.org
Tue Jul 20 13:54:48 UTC 2010


Author: pag
Date: 2010-07-20 13:54:47 +0000 (Tue, 20 Jul 2010)
New Revision: 1969

Added:
   trunk/lib/je-4.0.103.jar
   trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/
   trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbEnvironment.java
   trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuples.java
   trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesFactory.java
   trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesTest.java
Modified:
   trunk/.classpath
   trunk/build.properties
   trunk/build.xml
   trunk/common.properties
   trunk/common.xml
   trunk/conf/mulgara-rmi.policy
   trunk/conf/mulgara-test.policy
   trunk/conf/mulgara.policy
Log:
Added a new HybridTuples that uses BDB. Policies and path had to change for the BDB addition.

Modified: trunk/.classpath
===================================================================
--- trunk/.classpath	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/.classpath	2010-07-20 13:54:47 UTC (rev 1969)
@@ -91,6 +91,7 @@
 	<classpathentry kind="lib" path="lib/jasper-runtime-4.2.29.jar"/>
 	<classpathentry kind="lib" path="lib/javacc.jar"/>
 	<classpathentry kind="lib" path="lib/jboss-j2ee.jar"/>
+	<classpathentry kind="lib" path="lib/je-4.0.103.jar"/>
 	<classpathentry kind="lib" path="lib/jena-2.5.6.jar"/>
 	<classpathentry kind="lib" path="lib/jenatest-2.5.6.jar"/>
 	<classpathentry kind="lib" path="lib/interruptiblermi-1.1.jar"/>

Modified: trunk/build.properties
===================================================================
--- trunk/build.properties	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/build.properties	2010-07-20 13:54:47 UTC (rev 1969)
@@ -98,6 +98,7 @@
 aspectjrt.jar            =aspectjrt-1.2.jar
 axis.jar                 =axis-1.4.jar
 bsh.jar                  =bsh-2.0b1.jar
+bdb.jar                  =je-4.0.103.jar
 castor-xml.jar           =castor-1.2-xml.jar
 castor-xml-schema.jar    =castor-1.2-xml-schema.jar
 castor-codegen.jar       =castor-1.2-codegen.jar

Modified: trunk/build.xml
===================================================================
--- trunk/build.xml	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/build.xml	2010-07-20 13:54:47 UTC (rev 1969)
@@ -501,6 +501,7 @@
       <zipfileset src="${lib.dir}/${antlr.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${axis.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${activation.jar}" excludes="META-INF/**"/>
+      <zipfileset src="${lib.dir}/${bdb.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${carol.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-codegen.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-xml.jar}" excludes="META-INF/**"/>
@@ -1677,6 +1678,7 @@
       <zipfileset src="${lib.dir}/${activation.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${antlr.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${axis.jar}" excludes="META-INF/**, log4j.properties"/>
+      <zipfileset src="${lib.dir}/${bdb.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${carol.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-xml.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-xml-schema.jar}" excludes="META-INF/**"/>
@@ -2125,6 +2127,7 @@
       <zipfileset src="${lib.dir}/${activation.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${antlr.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${axis.jar}" excludes="META-INF/**, log4j.properties"/>
+      <zipfileset src="${lib.dir}/${bdb.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${carol.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-xml.jar}" excludes="META-INF/**"/>
       <zipfileset src="${lib.dir}/${castor-xml-schema.jar}" excludes="META-INF/**"/>

Modified: trunk/common.properties
===================================================================
--- trunk/common.properties	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/common.properties	2010-07-20 13:54:47 UTC (rev 1969)
@@ -5,7 +5,7 @@
 # Product properties
 project        =mulgara
 description    =Mulgara Semantic Store
-mulgara-version=2.1.8
+mulgara-version=2.1.9
 year           =2001-2010
 default.build.label=${description} Version ${mulgara-version} (Build @@build@@)
 

Modified: trunk/common.xml
===================================================================
--- trunk/common.xml	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/common.xml	2010-07-20 13:54:47 UTC (rev 1969)
@@ -143,7 +143,7 @@
                        ${jena.jar}, ${iri.jar}, ${icu4j.jar}, jxunit*.jar, castor*.jar,
                        mail-*.jar, activation-*.jar, apache-soap-*.jar, jline*.jar,
                        axis-*.jar, saaj-*.jar, emory-*.jar, ${trove.jar},
-                       jargs-*.jar, jetty-*.jar, jta-spec*.jar,
+                       ${bdb.jar}, jargs-*.jar, jetty-*.jar, jta-spec*.jar,
                        jotm*.jar, commons-logging-*.jar, Quick4rt.jar,
                        commons-discovery-*.jar, commons-httpclient-*.jar,
                        commons-codec-*.jar, connector-*.jar, jaxrpc-*.jar,

Modified: trunk/conf/mulgara-rmi.policy
===================================================================
--- trunk/conf/mulgara-rmi.policy	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/conf/mulgara-rmi.policy	2010-07-20 13:54:47 UTC (rev 1969)
@@ -18,6 +18,7 @@
   permission java.lang.RuntimePermission "setIO";
   permission java.lang.RuntimePermission "exitVM";
   permission java.lang.RuntimePermission "loadLibrary.keychain";
+  permission java.lang.management.ManagementPermission "monitor";
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
   permission java.net.SocketPermission "*:80", "listen,accept,connect,resolve";
   permission java.net.SocketPermission "*:443", "connect,resolve";
@@ -37,6 +38,7 @@
   permission java.util.PropertyPermission "triple.store.implementation", "write";
   permission java.util.PropertyPermission "descriptor.model", "write";
   permission java.util.PropertyPermission "*", "read";
+  permission java.util.logging.LoggingPermission "control";
   permission javax.security.auth.AuthPermission "createLoginContext.mulgara";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
   permission javax.security.auth.AuthPermission "modifyPrincipals";

Modified: trunk/conf/mulgara-test.policy
===================================================================
--- trunk/conf/mulgara-test.policy	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/conf/mulgara-test.policy	2010-07-20 13:54:47 UTC (rev 1969)
@@ -14,6 +14,7 @@
   permission java.lang.RuntimePermission "accessDeclaredMembers";
   permission java.lang.RuntimePermission "loadLibrary.keychain";
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+  permission java.lang.management.ManagementPermission "monitor";
   permission java.net.SocketPermission "*:1099", "connect,resolve";
   permission java.net.SocketPermission "*:1024-", "accept,connect,resolve";
   permission java.net.SocketPermission "*:443", "accept,connect,resolve";
@@ -26,6 +27,7 @@
   permission java.util.PropertyPermission "java.naming.provider.url", "write";
   permission java.util.PropertyPermission "java.rmi.server.codebase", "write";
   permission java.util.PropertyPermission "sun.arch.data.model", "read";
+  permission java.util.logging.LoggingPermission "control";
   permission javax.security.auth.AuthPermission "createLoginContext.mulgara";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
 

Modified: trunk/conf/mulgara.policy
===================================================================
--- trunk/conf/mulgara.policy	2010-07-06 21:50:25 UTC (rev 1968)
+++ trunk/conf/mulgara.policy	2010-07-20 13:54:47 UTC (rev 1969)
@@ -14,6 +14,7 @@
   permission java.lang.RuntimePermission "shutdownHooks";
   permission java.lang.RuntimePermission "setIO";
   permission java.lang.RuntimePermission "exitVM";
+  permission java.lang.management.ManagementPermission "monitor";
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
   permission java.net.SocketPermission "*:80", "accept,connect,resolve";
   permission java.net.SocketPermission "*:443", "connect,resolve";
@@ -31,6 +32,7 @@
   permission java.util.PropertyPermission "mail.smtp.host", "write";
   permission java.util.PropertyPermission "descriptor.model", "write";
   permission java.util.PropertyPermission "*", "read";
+  permission java.util.logging.LoggingPermission "control";
   permission javax.security.auth.AuthPermission "createLoginContext.mulgara";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
   permission javax.security.auth.AuthPermission "modifyPrincipals";

Added: trunk/lib/je-4.0.103.jar
===================================================================
(Binary files differ)


Property changes on: trunk/lib/je-4.0.103.jar
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbEnvironment.java
===================================================================
--- trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbEnvironment.java	                        (rev 0)
+++ trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbEnvironment.java	2010-07-20 13:54:47 UTC (rev 1969)
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2010, Paul Gearon
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mulgara.store.bdb;
+
+import org.apache.log4j.Logger;
+import org.mulgara.util.TempDir;
+
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.EnvironmentConfig;
+
+/**
+ * A central location for creating a singleton BerkeleyDB environment.
+ *
+ * @created Jul 12, 2010
+ * @author Paul Gearon
+ * @copyright &copy; 2009 <a href="http://www.duraspace.org/">DuraSpace</a>
+ */
+public class DbEnvironment {
+  
+  private final static Logger logger = Logger.getLogger(DbEnvironment.class);
+
+  static private Environment env;
+  static private EnvironmentConfig envCfg;
+
+  static Environment getEnv() {
+    if (env == null) {
+      try {
+        envCfg = new EnvironmentConfig();
+        envCfg.setAllowCreate(true);
+        env = new Environment(TempDir.getTempDir(), envCfg);
+      } catch (DatabaseException dbe) {
+        logger.error("Error creating BDB environment: " + dbe.getMessage(), dbe);
+        throw new RuntimeException("Unable to manage data with BDB", dbe);
+      }
+    }
+    return env;
+  }
+
+
+}

Added: trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuples.java
===================================================================
--- trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuples.java	                        (rev 0)
+++ trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuples.java	2010-07-20 13:54:47 UTC (rev 1969)
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2010, Paul Gearon
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mulgara.store.bdb;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Map;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.LongBuffer;
+
+import org.apache.log4j.Logger;
+
+import org.mulgara.query.Constraint;
+import org.mulgara.query.Cursor;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.Variable;
+import org.mulgara.store.statement.StatementStore;
+import org.mulgara.store.tuples.Annotation;
+import org.mulgara.store.tuples.RowComparator;
+import org.mulgara.store.tuples.SimpleTuplesFormat;
+import org.mulgara.store.tuples.Tuples;
+import org.mulgara.store.tuples.TuplesOperations;
+import org.mulgara.util.Constants;
+import org.mulgara.util.StackTrace;
+
+import com.sleepycat.je.Database;
+import com.sleepycat.je.DatabaseConfig;
+import com.sleepycat.je.DatabaseEntry;
+import com.sleepycat.je.DatabaseException;
+import com.sleepycat.je.Environment;
+import com.sleepycat.je.LockMode;
+import com.sleepycat.je.OperationStatus;
+
+/**
+ * Represents a group of tuples, sorted and stored in a Berkeley DB.
+ *
+ * @created 2010-07-11
+ * @author Paul Gearon
+ */
+public final class DbTuples implements Tuples {
+
+  /** A map from DbTuples objects to their comparators */
+  static private Map<Integer,RowComparator> comparators = new HashMap<Integer,RowComparator>();
+
+  protected final Variable[] vars;
+  protected final int width;
+  protected final RowComparator comparator;
+  protected final boolean unconstrained;
+  protected final boolean duplicates;
+  protected final boolean[] columnEverUnbound;
+  protected Tuples tuples;
+
+  // Would be final except for clone
+  // can be final once clone semantics migrated into CacheLine.
+  protected boolean beforeFirstCalled;
+  protected boolean nextCalled;
+
+  /** Used to indicate that beforeFirst has set the first record to be returned from next */
+  protected boolean initialRecord = false;
+  /** Indicates that beforeFirst found data to be returned */
+  protected boolean initialStatus = false;
+
+  /** The prefix used in the most recent beforeFirst */
+  protected long[] currentPrefix;
+
+  /** The database used to represent these tuples */
+  protected Database database;
+  /** The reference count to the database */
+  private int refCount = 0;
+
+  /** The internal cursor over the BDB table */
+  protected com.sleepycat.je.Cursor cursor;
+  /** The data to be read from the BDB table */
+  protected DatabaseEntry line = new DatabaseEntry();
+  /** The empty value object that is required for reading, but will always be empty. */
+  protected DatabaseEntry emptyVal = new DatabaseEntry();
+
+  /** The buffer containing the data from the current record of the tuples. */
+  protected LongBuffer tupleLine;
+
+  protected int[] varLookupList;
+  private boolean closed = false;
+
+  // Debugging.
+  private final static Logger logger = Logger.getLogger(DbTuples.class);
+  private StackTrace allocatedBy;
+  private StackTrace closedBy;
+
+  /**
+   * Constructs a tuples that represents another tuples with a given ordering.
+   * @param tuples The original tuples to represent.
+   * @param comparator The definition of the new ordering.
+   * @throws TuplesException If there is a problem accessing the original tuples.
+   */
+  protected DbTuples(Tuples tuples, RowComparator comparator) throws TuplesException {
+
+    if (logger.isDebugEnabled()) {
+      logger.debug("DbTuples created " + System.identityHashCode(this));
+    }
+
+    // store the comparator, and map to it by the current identifier
+    this.comparator = comparator;
+    comparators.put(System.identityHashCode(this), comparator);
+
+    // get some of tuples structure from the original tuples
+    this.vars = tuples.getVariables();
+    this.unconstrained = tuples.isUnconstrained();
+    this.duplicates = !tuples.hasNoDuplicates();
+
+    // Create a lookup up list of unique variables to their position in an index.
+    HashSet<Variable> uniqueVars = new HashSet<Variable>();
+    List<Variable> uniqueVarIndex = new ArrayList<Variable>();
+    varLookupList = new int[vars.length];
+    int varIndex = -1;
+    for (int index = 0; index < vars.length; index++) {
+
+      // Add variable to set.
+      uniqueVars.add(vars[index]);
+
+      // Check to see if variable is already in list, if not add to list.  Set
+      // lookup list to current variable index value.
+      int indexPos = uniqueVarIndex.indexOf(vars[index]);
+      if (indexPos == -1) {
+        uniqueVarIndex.add(vars[index]);
+        varIndex++;
+        indexPos = varIndex;
+      }
+      varLookupList[index] = indexPos;
+    }
+
+    this.width = uniqueVars.size();
+    this.columnEverUnbound = new boolean[this.width];
+    Arrays.fill(this.columnEverUnbound, false);
+    
+    // put everything into a BDB
+    database = createTempDb();
+    refCount++;  // created the first reference to database
+    materialiseTuples(tuples, database);
+
+    // set up some state on cursors
+    this.beforeFirstCalled = false;
+    this.nextCalled = false;
+
+    // Set up details for debugging, if necessary
+    if (logger.isDebugEnabled()) {
+      this.tuples = (Tuples)tuples.clone();
+    } else {
+      this.tuples = TuplesOperations.empty();
+    }
+    if (logger.isDebugEnabled()) this.allocatedBy = new StackTrace();
+  }
+
+
+  /**
+   * Required by Tuples, Cursor.
+   */
+  public boolean next() throws TuplesException {
+    if (!beforeFirstCalled) {
+      logger.error("next() called before beforeFirst()");
+      throw new TuplesException("next() called before beforeFirst()");
+    }
+
+    boolean status;
+    if (initialRecord) {
+      // beforeFirst already read the record, so just extract it and test it
+      initialRecord = false;
+      if (!initialStatus) status = false;
+      else {
+        tupleLine = ByteBuffer.wrap(line.getData()).asLongBuffer();
+        status = testLine(tupleLine, currentPrefix);
+      }
+    } else {
+      // subsequent record, so move to the next and test it
+      status = cursor.getNext(line, emptyVal, LockMode.DEFAULT) == OperationStatus.SUCCESS;
+      if (status) {
+        // extract the current record
+        tupleLine = ByteBuffer.wrap(line.getData()).asLongBuffer();
+        // check to see if the cursor has stepped past the matching data
+        status = testLine(tupleLine, currentPrefix);
+      }
+    }
+    nextCalled = true;
+    return status;
+  }
+
+
+  /**
+   * Required by Tuples.
+   */
+  public void beforeFirst(long[] prefix, int suffixTruncation) throws TuplesException {
+    assert prefix != null;
+
+    if (suffixTruncation != 0) {
+      logger.error("DbTuples.beforeFirst(suffix) unimplemented");
+      throw new IllegalArgumentException("DbTuples.beforeFirst(suffix) unimplemented");
+    }
+ 
+    // copy the prefix so we know when we've left the search range
+    currentPrefix = new long[prefix.length];
+    System.arraycopy(prefix, 0, currentPrefix, 0, prefix.length);
+
+    // get a new cursor
+    if (cursor == null) cursor = database.openCursor(null, null);
+
+    // construct the buffer that cursor will search on
+    ByteBuffer bb = ByteBuffer.allocate(width * Constants.SIZEOF_LONG);
+    LongBuffer lb = bb.asLongBuffer();
+    for (int i = 0; i < prefix.length; i++) lb.put(i, prefix[i]);
+    line.setData(bb.array());
+    initialStatus = cursor.getSearchKey(line, emptyVal, LockMode.DEFAULT) == OperationStatus.SUCCESS;
+
+    // remember state
+    initialRecord = true;
+    beforeFirstCalled = true;
+    nextCalled = false;
+  }
+
+
+  /**
+   * Required by Tuples.
+   */
+  public long getColumnValue(int column) throws TuplesException {
+    if (column < 0 || column >= width) {
+      throw new TuplesException("No column " + column + " in " + Arrays.asList(vars));
+    }
+
+    if (!nextCalled) throw new TuplesException("getColumnValue() called before next()");
+
+    return tupleLine.get(varLookupList[column]);
+  }
+
+
+  /**
+   * Required by Tuples.
+   */
+  public void renameVariables(Constraint constraint) {
+    for (int i = 0; i < vars.length; i++) {
+      Variable v = vars[i];
+      boolean found = false;
+      for (int j = 0; j < 4; j++) {
+        // v will be a reference to one of the objects in Graph.VARIABLES[]
+        if (v == StatementStore.VARIABLES[j]) {
+          // The array obtained from getVariables() is modifiable.
+          vars[i] = (Variable)constraint.getElement(j);
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        logger.error("Unexpected variable: " + v);
+        throw new Error("Unexpected variable: " + v);
+      }
+    }
+  }
+
+
+  /**
+   * Required by Tuples.
+   */
+  public Object clone() {
+    try {
+      DbTuples copy = (DbTuples)super.clone();
+      // we now have a new reference to database
+      refCount++;
+      if (logger.isDebugEnabled()) copy.allocatedBy = new StackTrace();
+      copy.tuples = (Tuples)tuples.clone();
+      copy.currentPrefix = currentPrefix == null ? null : currentPrefix.clone();
+      copy.line = new DatabaseEntry(line.getData());
+      if (tupleLine != null ) {
+        tupleLine.position(0);
+        copy.tupleLine = ByteBuffer.allocate(width * Constants.SIZEOF_LONG)
+                         .asLongBuffer().put(tupleLine);
+      }
+
+      return copy;
+    } catch (CloneNotSupportedException ce) {
+      logger.error("DbTuples.clone() threw CloneNotSupported", ce);
+      throw new RuntimeException("DbTuples.clone() threw CloneNotSupported", ce);
+    }
+  }
+
+
+  /**
+   * This could fall back to beforeFirst(Tuples.NO_PREFIX), but this is more efficient
+   */
+  public void beforeFirst() throws TuplesException {
+    currentPrefix = Tuples.NO_PREFIX;
+
+    // get a new cursor
+    if (cursor == null) cursor = database.openCursor(null, null);
+
+    initialStatus = cursor.getFirst(line, emptyVal, LockMode.DEFAULT) == OperationStatus.SUCCESS;
+
+    // remember state
+    initialRecord = true;
+    beforeFirstCalled = true;
+    nextCalled = false;
+  }
+
+
+  /** @see org.mulgara.store.tuples.Tuples#getOperands() */
+  public List<Tuples> getOperands() {
+    return Collections.singletonList(tuples);
+  }
+
+  /**
+   * Required by Cursor.
+   */
+  public void close() throws TuplesException {
+    if (closed) {
+      if (logger.isDebugEnabled()) {
+        logger.debug("Attempt to close DbTuples twice; first closed: " + closedBy);
+        logger.debug("Attempt to close DbTuples twice; second closed: " + new StackTrace());
+        logger.debug("    allocated: " + allocatedBy);
+      } else {
+        logger.error("Attempt to close HybridTuples twice. Enable debug to trace how.");
+      }
+      throw new TuplesException("Attempted to close HybribTuples more than once");
+    }
+    closed = true;
+    if (logger.isDebugEnabled()) closedBy = new StackTrace();
+
+    comparators.remove(System.identityHashCode(this));
+    try {
+      tuples.close();
+      tuples = null;
+    } finally {
+      try {
+        if (cursor != null) {
+          cursor.close();
+          cursor = null;
+        }
+      } finally {
+        assert refCount > 0 : "Released all BDB reference counts before closing Tuples";
+        refCount--;
+        if (refCount == 0 && database != null) {
+          database.close();
+          database = null;
+        }
+      }
+    }
+  }
+
+
+  /** {@inheritDoc} */
+  public int getColumnIndex(Variable variable) throws TuplesException {
+    for (int c = 0; c < vars.length; c++) if (vars[c].equals(variable)) return c;
+
+    logger.warn("Variable not found: " + variable);
+    throw new TuplesException("Variable not found: " + variable);
+  }
+
+
+  /** {@inheritDoc} */
+  public long getRawColumnValue(int column) throws TuplesException {
+    return UNBOUND;
+  }
+
+
+  /** {@inheritDoc} */
+  public int getNumberOfVariables() {
+    return vars != null ? vars.length : 0;
+  }
+
+
+  /** {@inheritDoc} */
+  public Variable[] getVariables() {
+    return vars;
+  }
+
+
+  /** {@inheritDoc} */
+  public boolean isUnconstrained() throws TuplesException {
+    return unconstrained;
+  }
+
+
+  /** {@inheritDoc} */
+  public long getRowCount() throws TuplesException {
+    return database.count();
+  }
+
+
+  public long getRowUpperBound() throws TuplesException {
+    return getRowCount();
+  }
+
+
+  public long getRowExpectedCount() throws TuplesException {
+    return getRowCount();
+  }
+
+
+  public int getRowCardinality() throws TuplesException {
+    switch ((int)getRowCount()) {
+      case 0:
+        return Cursor.ZERO;
+      case 1:
+        return Cursor.ONE;
+      default:
+        return Cursor.MANY;
+    }
+  }
+
+
+  public boolean isEmpty() throws TuplesException {
+    return getRowCount() == 0;
+  }
+
+
+  /** {@inheritDoc} */
+  public boolean isColumnEverUnbound(int column) throws TuplesException {
+    try {
+      return columnEverUnbound[column];
+    } catch (ArrayIndexOutOfBoundsException e) {
+      throw new TuplesException("No such column "+column);
+    }
+  }
+
+
+  /** {@inheritDoc} */
+  public boolean isMaterialized() {
+    return true;
+  }
+
+
+  /** {@inheritDoc} */
+  public boolean hasNoDuplicates() {
+    return !duplicates;
+  }
+
+
+  /** {@inheritDoc} */
+  public RowComparator getComparator() {
+    return comparator;
+  }
+
+  /** Get the comparator for the given DbTuples ID */
+  public static RowComparator getCmp(int id) {
+    return comparators.get(id);
+  }
+
+  /** {@inheritDoc} */
+  public boolean equals(Object o) {
+    Tuples t;
+    Tuples c;
+
+    if (o == this) return true;
+
+    if (!(o instanceof Tuples)) return false;
+
+    t = (Tuples)o;
+    if (t instanceof DbTuples) return this.database.equals(((DbTuples)t).database);
+
+    try {
+      Variable[] tvars = t.getVariables();
+      if (this.getRowCount() != t.getRowCount() || this.vars.length != tvars.length) {
+        return false;
+      }
+
+      for (int v = 0; v < this.width; v++) {
+        if (!this.vars[v].equals(tvars[v])) return false;
+      }
+
+      t = (Tuples)t.clone();
+      c = (Tuples)this.clone();
+
+      try {
+        t.beforeFirst();
+        c.beforeFirst();
+
+        while(true) {
+          boolean tn = t.next();
+          boolean cn = c.next();
+          if (!tn && !cn) return true;
+          if ((!tn && cn) || (tn && !cn)) return false;
+          for (int i = 0; i < width; i++) {
+            if (t.getColumnValue(i) != c.getColumnValue(i)) return false;
+          }
+        }
+      } finally {
+        t.close();
+        c.close();
+      }
+    } catch (TuplesException te) {
+      throw new RuntimeException("Tuples Exception in HybridTuples.equals", te);
+    }
+  }
+
+
+  /** {@inheritDoc} */
+  public String toString() {
+    return SimpleTuplesFormat.format(this);
+  }
+
+
+  /**
+   * Load all of the tuples into the local database.
+   * @param tuples The tuples to load.
+   * @param db The database to insert into.
+   * @return The number of insertions made. This will be the row count if duplicates are allowed.
+   * @throws TuplesException Due to an error reading from the tuples.
+   */
+  private long materialiseTuples(Tuples tuples, Database db) throws TuplesException {
+    tuples.beforeFirst();
+
+    ByteBuffer bb = ByteBuffer.allocate(width * Constants.SIZEOF_LONG);
+    LongBuffer lb = bb.asLongBuffer();
+    byte[] array = bb.array();
+    DatabaseEntry key = new DatabaseEntry(array);
+
+    byte[] valArray;
+    LongBuffer valLB = null;
+    if (duplicates) {
+      ByteBuffer valB = ByteBuffer.allocate(Constants.SIZEOF_LONG);
+      valLB = valB.asLongBuffer();
+      valArray = valB.array();
+    } else {
+      valArray = new byte[0];
+    }
+    // The ignored value. If we can have duplicates, then make this an incrementing number.
+    DatabaseEntry emptyVal = new DatabaseEntry(valArray);
+
+    long count = 0;
+    while (tuples.next()) {
+      for (int i = 0; i < width; i++) lb.put(i, tuples.getColumnValue(i));
+      if (duplicates) valLB.put(0, count);
+      db.put(null, key, emptyVal);
+      count++;
+    }
+    return count;
+  }
+
+  private Database createTempDb() throws TuplesException {
+    Environment env = DbEnvironment.getEnv();
+    try {
+      DatabaseConfig dbCfg = new DatabaseConfig();
+      dbCfg.setAllowCreate(true);
+      dbCfg.setTemporary(true);
+      dbCfg.setSortedDuplicates(duplicates);
+      int id = System.identityHashCode(this);
+      dbCfg.setBtreeComparator(new DbComparator(id, comparator));
+      return env.openDatabase(null, "tuples_" + id, dbCfg);
+    } catch (DatabaseException dbe) {
+      throw new TuplesException("Error creating BDB database: " + dbe.getMessage());
+    }
+
+  }
+
+  /**
+   * Tests if a long buffer matches a long array, starting at the end of the array
+   * and working backwards. Only the length of the array is tested.
+   * @param l The buffer to compare.
+   * @param p The array to compare. This also sets the comparison length.
+   * @return <code>true</code> if all the elements of the array match the corresponding
+   *         elements in the buffer.
+   */
+  private static final boolean testLine(LongBuffer l, long[] p) {
+    for (int i = p.length - 1; i >= 0; i--) {
+      if (l.get(i) != p[i]) return false;
+    }
+    return true;
+  }
+
+
+  /** Copied from AbstractTuples */
+  public Annotation getAnnotation(Class<? extends Annotation> annotationClass) throws TuplesException {
+    return null;
+  }
+
+
+  public static class DbComparator implements Comparator<byte[]>, Serializable {
+
+    /** The serialization ID */
+    private static final long serialVersionUID = -2986622291706235593L;
+
+    /** An internal identifier */
+    private final Integer id;
+
+    /** The wrapped RowComparator that does the actual work */
+    private transient RowComparator rc;
+
+    /**
+     * Creates a comparator, based on a row comparator
+     * @param rc The comparator to do the real work.
+     */
+    public DbComparator(int id, RowComparator rc) {
+      this.id = id;
+      this.rc = rc;
+    }
+
+    /** @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */
+    public int compare(byte[] o1, byte[] o2) {
+      try {
+        LongBuffer lb1 = ByteBuffer.wrap(o1).asLongBuffer();
+        LongBuffer lb2 = ByteBuffer.wrap(o2).asLongBuffer();
+        long[] l1 = new long[lb1.capacity()];
+        long[] l2 = new long[lb2.capacity()];
+        lb1.get(l1);
+        lb2.get(l2);
+        return rc.compare(l1, l2);
+      } catch (TuplesException e) {
+        throw new RuntimeException("Error reading values for comparison in the BDB table", e);
+      }
+    }
+
+    /** The identifier assigned to this comparator */
+    public Integer getId() {
+      return id;
+    }
+
+    /**
+     * After reading back this object, find the row comparator that was assigned for it.
+     * @param in The input stream to get the object from.
+     * @throws IOException Error reading from the stream.
+     * @throws ClassNotFoundException If the class in the stream is not in the classpath.
+     */
+    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+      in.defaultReadObject();
+      rc = getCmp(id);
+    }
+
+  }
+
+}
+

Added: trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesFactory.java
===================================================================
--- trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesFactory.java	                        (rev 0)
+++ trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesFactory.java	2010-07-20 13:54:47 UTC (rev 1969)
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2010, Paul Gearon
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.mulgara.store.bdb;
+
+
+import org.mulgara.query.TuplesException;
+import org.mulgara.store.tuples.DefaultRowComparator;
+import org.mulgara.store.tuples.RowComparator;
+import org.mulgara.store.tuples.Tuples;
+import org.mulgara.store.tuples.TuplesFactory;
+
+/**
+ * Generates {@link BdTuples} instances.
+ *
+ * @created 2010-07-12
+ * @author Paul Gearon
+ */
+public class DbTuplesFactory extends TuplesFactory {
+
+  /** The default ordering for tuples is by node number. */
+  private RowComparator rowComparator = new DefaultRowComparator();
+
+
+  /**
+   * Copy constructor.
+   *
+   * @param tuples an existing instance, whose contents will be copied into the new instance
+   * @return a new {@link HybridTuples} instance
+   * @throws IllegalArgumentException if <var>tuples</var> is <code>null</code>
+   * @throws TuplesException if a copy of <var>tuples</var> can't be created
+   */
+  public Tuples newTuples(Tuples tuples) throws TuplesException {
+    return newTuples(tuples, rowComparator);
+  }
+
+
+  /**
+   * Construct a new tuples with a specified order.
+   *
+   * @param tuples an existing instance, whose contents will be copied into the
+   *      new instance
+   * @param rowComparator the desired sort order
+   * @return a new {@link HybridTuples} instance
+   * @throws IllegalArgumentException if <var>tuples</var> or <var>rowComparator
+   *      </var> parameters are <code>null</code>
+   * @throws TuplesException if a copy of <var>tuples</var> can't be created
+   */
+  public Tuples newTuples(Tuples tuples, RowComparator rowComparator) throws TuplesException {
+    if (rowComparator == null) {
+      throw new IllegalArgumentException("Null \"rowComparator\" parameter");
+    }
+    if (tuples == null) {
+      throw new IllegalArgumentException("Null \"tuples\" parameter");
+    }
+
+    return new DbTuples(tuples, rowComparator);
+  }
+}

Added: trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesTest.java
===================================================================
--- trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesTest.java	                        (rev 0)
+++ trunk/src/jar/tuples-hybrid/java/org/mulgara/store/bdb/DbTuplesTest.java	2010-07-20 13:54:47 UTC (rev 1969)
@@ -0,0 +1,658 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+
+package org.mulgara.store.bdb;
+
+// Third party packages
+import junit.framework.*;
+
+import java.io.*;
+
+// Java 2 standard packages
+import java.util.*;
+
+import org.apache.log4j.Logger;
+
+// Locally written packages.
+import org.mulgara.query.Variable;
+import org.mulgara.store.tuples.DefaultRowComparator;
+import org.mulgara.store.tuples.LiteralTuples;
+import org.mulgara.store.tuples.RowComparator;
+
+/**
+ * Test cases for DbTuples.
+ *
+ * @created 2003-02-04
+ *
+ * @author Paul Gearon
+ *
+ * @version $Revision: 1.9 $
+ *
+ * @modified $Date: 2005/01/05 04:59:12 $
+ *
+ * @maintenanceAuthor $Author: newmana $
+ *
+ * @company <A href="mailto:info at PIsoftware.com">Plugged In Software</A>
+ *
+ * @copyright &copy;2004 <a href="http://www.pisoftware.com/">Plugged In
+ *      Software Pty Ltd</a>
+ *
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+public class DbTuplesTest extends TestCase {
+  // Larger than this and in memory structures used by the tests fail with OOM
+//  private final static int LOAD_SIZE = 262142;
+  private final static int LOAD_SIZE = 1000000;
+  private final static int ODD_LOAD_SIZE = 200000;
+  private final static int BF_SMALL_LOAD_SIZE = 10;
+  private final static int BF_LARGE_LOAD_SIZE = 100;
+  private final static Logger logger = Logger.getLogger(DbTuplesTest.class);
+  private final static int WIDTH = 3;
+  private DbTuples dbTuples;
+  private RowComparator rowComparator;
+
+  private final static Comparator<long[]> longSingletonArrayComparator =  new Comparator<long[]>() {
+        public int compare(long[] a1, long[] a2) {
+          return a1[0] == a2[0] ? 0 : (a1[0] < a2[0] ? -1 : +1);
+        }
+      };
+
+  /**
+   * Named constructor.
+   *
+   * @param name The name of the test.
+   */
+  public DbTuplesTest(String name) {
+    super(name);
+  }
+
+
+  /**
+   * Hook for test runner to obtain a test suite from.
+   *
+   * @return The test suite to run.
+   */
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new DbTuplesTest("testCreate"));
+    suite.addTest(new DbTuplesTest("testSort"));
+    suite.addTest(new DbTuplesTest("testOddLoad"));
+    suite.addTest(new DbTuplesTest("testLoadSort"));
+    suite.addTest(new DbTuplesTest("testEmpty"));
+    suite.addTest(new DbTuplesTest("testLargeOrdered"));
+    suite.addTest(new DbTuplesTest("testCloneAndClose"));
+
+    // determine why these do not work
+    // suite.addTest(new DbTuplesTest("testPositioning"));
+    // suite.addTest(new DbTuplesTest("testSmallBeforeFirst"));
+    // suite.addTest(new DbTuplesTest("testLargeBeforeFirst"));
+
+    return suite;
+  }
+
+
+  /**
+   * Default test runner.
+   *
+   * @param args The command line arguments
+   */
+  public static void main(String[] args) {
+    junit.textui.TestRunner.run(suite());
+  }
+
+
+  /**
+   * Creates a new file required to do the testing.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void setUp() throws Exception {
+    boolean exceptionOccurred = true;
+    try {
+      rowComparator = DefaultRowComparator.getInstance();
+      exceptionOccurred = false;
+    } finally {
+      if (exceptionOccurred) {
+        tearDown();
+      }
+    }
+  }
+
+
+  /**
+   * Closes the file used for testing.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void tearDown() throws Exception {
+    if (dbTuples != null) {
+      dbTuples.close();
+      dbTuples = null;
+    }
+  }
+
+
+  /**
+   * Test creation based on a 6 element set.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void testCreate() throws Exception {
+    LiteralTuples tt = new LiteralTuples(new String[] { "test" });
+    tt.appendTuple(new long[] { 1 });
+    tt.appendTuple(new long[] { 2 });
+    tt.appendTuple(new long[] { 3 });
+    tt.appendTuple(new long[] { 4 });
+    tt.appendTuple(new long[] { 5 });
+    tt.appendTuple(new long[] { 6 });
+
+    dbTuples = new DbTuples(tt, rowComparator);
+
+    dbTuples.beforeFirst();
+
+    // check that the order is as expected
+    long row = 1;
+    while (dbTuples.next()) {
+      assertEquals(row++, dbTuples.getColumnValue(0));
+    }
+
+    // check for the correct number of elements in dbTuples
+    assertTrue(row == 7);
+  }
+
+
+  /**
+   * Test sorting
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void testSort() throws Exception {
+    LiteralTuples tt = new LiteralTuples(new String[] { "test" });
+    tt.appendTuple(new long[] { 6 });
+    tt.appendTuple(new long[] { 5 });
+    tt.appendTuple(new long[] { 4 });
+    tt.appendTuple(new long[] { 3 });
+    tt.appendTuple(new long[] { 2 });
+    tt.appendTuple(new long[] { 1 });
+
+    dbTuples = new DbTuples(tt, rowComparator);
+
+    dbTuples.beforeFirst();
+    // check that the order is as expected
+    long row = 1;
+    while (dbTuples.next()) {
+      assertEquals(row++, dbTuples.getColumnValue(0));
+    }
+
+    // check for the correct number of elements in dbTuples
+    assertTrue(row == 7);
+  }
+
+
+  /**
+   * Test cloneing DbTuples and closing original copy.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void testCloneAndClose() throws Exception {
+    LiteralTuples tt = new LiteralTuples(new String[] { "test" });
+    tt.appendTuple(new long[] { 6 });
+    tt.appendTuple(new long[] { 5 });
+    tt.appendTuple(new long[] { 4 });
+    tt.appendTuple(new long[] { 3 });
+    tt.appendTuple(new long[] { 2 });
+    tt.appendTuple(new long[] { 1 });
+
+    DbTuples origTuples = new DbTuples(tt, rowComparator);
+    dbTuples = (DbTuples)origTuples.clone();
+    origTuples.close();
+
+    dbTuples.beforeFirst();
+    // check that the order is as expected
+    long row = 1;
+    while (dbTuples.next()) {
+      assertEquals(row++, dbTuples.getColumnValue(0));
+    }
+
+    // check for the correct number of elements in dbTuples
+    assertTrue(row == 7);
+  }
+
+
+  /**
+   * Load test the sorting
+   *
+   * @throws Throwable EXCEPTION TO DO
+   */
+  public void testLoadSort() throws Throwable {
+    Random r = new Random(42);    // use a constant seed
+    long[] v = new long[] { r.nextLong() & 0xFFFFFFFFL };
+    LiteralTuples tt = new LiteralTuples(new String[] { "test" });
+    tt.appendTuple(v);
+
+    ArrayList<long[]> values = new ArrayList<long[]>();
+    values.add(v);
+
+    // iterate over a large enough range to guarantee several blocks
+    for (int i = 0; i < LOAD_SIZE; i++) {
+      v = new long[] { r.nextLong() & 0xFFFFFFFFL };
+      if (v[0] == 0) {
+        v[0]++;
+      }
+      values.add(v);
+      tt.appendTuple(v);
+    }
+    Collections.sort(values, longSingletonArrayComparator);
+
+//    logger.info("LoadSort: Creating DbTuples");
+    dbTuples = new DbTuples(tt, rowComparator);
+//    logger.info("LoadSort: BeforeFirst");
+    dbTuples.beforeFirst();
+
+//    logger.info("LoadSort: Checking Order");
+    // check that the order is as expected
+    Iterator<long[]> it = values.iterator();
+    int i = 0;
+    try {
+      while (it.hasNext()) {
+        assertEquals("On iteration " + i, dbTuples.next(), true);
+        v = it.next();
+        i++;
+//        if (i % 10000 == 0) {
+//          logger.info("Checked " + i + " tuples");
+//        }
+        assertEquals("On iteration " + i, v[0], dbTuples.getColumnValue(0));
+      }
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+
+//    logger.info("LoadSort: Finished order check");
+    // check that there aren't any extraneous values
+    assertTrue(!dbTuples.next());
+//    logger.info("LoadSort: Finished test");
+  }
+
+
+  /**
+   * Load test odd sized tuples
+   *
+   * @throws Throwable EXCEPTION TO DO
+   */
+  public void testOddLoad() throws Throwable {
+    Variable[] vars = new Variable[WIDTH];
+
+    for (int c = 0; c < WIDTH; c++) {
+      vars[c] = new Variable("test" + (c + 1));
+    }
+
+    // use a constant seed
+    Random r = new Random(42);
+
+    LiteralTuples tt = new LiteralTuples(vars);
+    long[] v = new long[WIDTH];
+
+    // iterate over a large enough range to guarantee several blocks
+    for (int i = 0; i < ODD_LOAD_SIZE; i++) {
+      for (int c = 0; c < WIDTH; c++) {
+        v[c] = r.nextLong() & 0x0000FFFFL;
+        if (v[c] == 0) {
+          v[c]++;
+        }
+      }
+
+      tt.appendTuple(v);
+    }
+
+    dbTuples = new DbTuples(tt, rowComparator);
+    dbTuples.beforeFirst();
+
+    // check that the order is as expected
+    long[] tuple = new long[WIDTH];
+    long[] lastTuple = new long[WIDTH];
+
+    for (int c = 0; c < WIDTH; c++) {
+      lastTuple[c] = 0;
+    }
+
+    int i = 0;
+    try {
+      while (dbTuples.next()) {
+        for (int c = 0; c < WIDTH; c++) {
+          tuple[c] = dbTuples.getColumnValue(c);
+        }
+
+        for (int c = 0; c < WIDTH; c++) {
+          if (tuple[c] < lastTuple[c]) {
+            logger.error("Error tuple[" + tuple[0] + "," + tuple[1] + "," +
+            tuple[2] + "] < lastTuple[" + lastTuple[0] + "," + lastTuple[1] +
+            "," + lastTuple[2] + "]");
+          }
+          assertTrue(tuple[c] >= lastTuple[c]);
+          if (tuple[c] != lastTuple[c]) {
+            break;
+          }
+        }
+
+        for (int c = 0; c < WIDTH; c++) {
+          lastTuple[c] = tuple[c];
+        }
+
+        i++;
+      }
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+
+    // check that there aren't any extraneous values
+//    logger.warn("i = " + i + ", LOAD_SIZE = " + ODD_LOAD_SIZE);
+    assertTrue(i == ODD_LOAD_SIZE);
+  }
+
+
+  /**
+   * Test adding an empty tuple
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void testEmpty() throws Exception {
+    LiteralTuples tt = new LiteralTuples(new Variable[] {});
+
+    dbTuples = new DbTuples(tt, rowComparator);
+    dbTuples.beforeFirst();
+
+    // check that the order is as expected
+    assertTrue(!dbTuples.next());
+  }
+
+
+  /**
+   * Test the {@link DbTuples#beforeFirst(long[], int)} and {@link
+   * DbTuples#next} methods.
+   *
+   * @throws Exception EXCEPTION TO DO
+   */
+  public void testPositioning() throws Exception {
+    LiteralTuples tt = new LiteralTuples(new String[] { "x", "y" });
+    tt.appendTuple(new long[] { 1, 2 });
+    tt.appendTuple(new long[] { 3, 4 });
+    tt.appendTuple(new long[] { 5, 6 });
+    tt.appendTuple(new long[] { 7, 8 });
+    dbTuples = new DbTuples(tt, rowComparator);
+
+    dbTuples.beforeFirst(new long[] {7, 8} , 0);
+    assertTrue(dbTuples.next());
+    assertEquals(7, dbTuples.getColumnValue(0));
+    assertEquals(8, dbTuples.getColumnValue(1));
+    assertTrue(!dbTuples.next());
+
+    dbTuples.beforeFirst();
+
+    assertTrue(dbTuples.next());
+    assertEquals(1, dbTuples.getColumnValue(0));
+    assertEquals(2, dbTuples.getColumnValue(1));
+
+    dbTuples.beforeFirst(new long[] {7, 8} , 0);
+    assertTrue(dbTuples.next());
+    assertEquals(7, dbTuples.getColumnValue(0));
+    assertEquals(8, dbTuples.getColumnValue(1));
+
+    assertTrue(!dbTuples.next());
+
+    dbTuples.beforeFirst(new long[] {1, 2} , 0);
+
+    assertTrue(dbTuples.next());
+    assertEquals(1, dbTuples.getColumnValue(0));
+    assertEquals(2, dbTuples.getColumnValue(1));
+
+    assertTrue(!dbTuples.next());
+
+    dbTuples.beforeFirst(new long[] {2} , 0);
+    assertTrue(!dbTuples.next());
+
+    dbTuples.beforeFirst(new long[] {3} , 0);
+    assertTrue(dbTuples.next());
+    assertEquals(3, dbTuples.getColumnValue(0));
+    assertEquals(4, dbTuples.getColumnValue(1));
+
+    assertTrue(!dbTuples.next());
+
+    dbTuples.close();
+    dbTuples = null;
+  }
+
+
+  public void testLargeOrdered() throws Throwable {
+    LiteralTuples tt = new LiteralTuples(new String[] { "test" });
+
+
+    ArrayList<long[]> values = new ArrayList<long[]>();
+
+    long count = 0;
+    // iterate over a large enough range to guarantee several blocks
+    for (int i = 0; i < LOAD_SIZE; i++) {
+      long[] v = new long[] { count++ };
+      v[0] = count++;
+      values.add(v);
+      tt.appendTuple(v);
+    }
+    Collections.sort(values, longSingletonArrayComparator);
+
+    dbTuples = new DbTuples(tt, rowComparator);
+    dbTuples.beforeFirst();
+
+    // check that the order is as expected
+    Iterator<long[]> it = values.iterator();
+    int i = 0;
+    try {
+      while (it.hasNext()) {
+        assertTrue(dbTuples.next());
+        long[] v = it.next();
+        i++;
+        assertEquals("On iteration " + i, v[0], dbTuples.getColumnValue(0));
+      }
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+
+    // check that there aren't any extraneous values
+    assertTrue(!dbTuples.next());
+  }
+
+
+  public void testSmallBeforeFirst() throws Throwable {
+    logger.warn("testSmallBeforeFirst");
+    LiteralTuples tt = new LiteralTuples(new String[] { "i", "j", "k" });
+
+    logger.warn("testSmallBeforeFirst: checkpoint 1");
+    // iterate over a large enough range to guarantee several blocks
+    for (int i = (BF_SMALL_LOAD_SIZE * 2) - 1; i > 0; i -= 2) {
+      for (int j = (BF_SMALL_LOAD_SIZE * 2) - 1; j > 0; j -= 2) {
+        for (int k = (BF_SMALL_LOAD_SIZE * 2) - 1; k > 0; k -= 2) {
+          long[] v = new long[] { i, j, k };
+          tt.appendTuple(v);
+        }
+      }
+    }
+
+    logger.warn("testSmallBeforeFirst: checkpoint 2");
+    dbTuples = new DbTuples(tt, rowComparator);
+
+    logger.warn("testSmallBeforeFirst: checkpoint 3");
+    dbTuples.beforeFirst();
+    // check that the order is as expected
+    logger.warn("testSmallBeforeFirst: checkpoint 4");
+    try {
+      long count = 0;
+      for (int i = 1; i < BF_SMALL_LOAD_SIZE * 2; i += 2) {
+        for (int j = 1; j < BF_SMALL_LOAD_SIZE * 2; j += 2) {
+          for (int k = 1; k < BF_SMALL_LOAD_SIZE * 2; k += 2) {
+            count++;
+            assertTrue(dbTuples.next());
+            assertEquals("On iteration " + count, i, dbTuples.getColumnValue(0));
+            assertEquals("On iteration " + count, j, dbTuples.getColumnValue(1));
+            assertEquals("On iteration " + count, k, dbTuples.getColumnValue(2));
+          }
+        }
+      }
+
+      // check that there aren't any extraneous values
+      assertTrue(!dbTuples.next());
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+    logger.warn("testSmallBeforeFirst: checkpoint 5");
+
+    dbTuples.beforeFirst(new long[] { 3, 5 }, 0);
+    logger.warn("testSmallBeforeFirst: checkpoint 6");
+    // check that the order is as expected
+    try {
+      long count = 0;
+      int i = 3;
+      int j = 5;
+      for (int k = 1; k < BF_SMALL_LOAD_SIZE * 2; k += 2) {
+        count++;
+        assertTrue(dbTuples.next());
+        assertEquals("On iteration " + count, i, dbTuples.getColumnValue(0));
+        assertEquals("On iteration " + count, j, dbTuples.getColumnValue(1));
+        assertEquals("On iteration " + count, k, dbTuples.getColumnValue(2));
+      }
+
+      // check that there aren't any extraneous values
+      assertTrue(!dbTuples.next());
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+    logger.warn("testSmallBeforeFirst: checkpoint 7");
+
+    dbTuples.beforeFirst(new long[] { 3, 4 }, 0);
+    logger.warn("testSmallBeforeFirst: checkpoint 8");
+    assertFalse(dbTuples.next());
+  }
+
+
+  public void testLargeBeforeFirst() throws Throwable {
+    logger.info("testLargeBeforeFirst");
+
+    LiteralTuples tt = new LiteralTuples(new String[] { "i", "j", "k" });
+
+    // iterate over a large enough range to guarantee several blocks
+    for (int i = (BF_LARGE_LOAD_SIZE * 2) - 1; i > 0; i -= 2) {
+      for (int j = (BF_LARGE_LOAD_SIZE * 2) - 1; j > 0; j -= 2) {
+        for (int k = (BF_LARGE_LOAD_SIZE * 2) - 1; k > 0; k -= 2) {
+          long[] v = new long[] { i, j, k };
+          tt.appendTuple(v);
+        }
+      }
+    }
+
+    dbTuples = new DbTuples(tt, rowComparator);
+
+    dbTuples.beforeFirst();
+    // check that the order is as expected
+    try {
+      long count = 0;
+      for (int i = 1; i < BF_LARGE_LOAD_SIZE * 2; i += 2) {
+        for (int j = 1; j < BF_LARGE_LOAD_SIZE * 2; j += 2) {
+          for (int k = 1; k < BF_LARGE_LOAD_SIZE * 2; k += 2) {
+            count++;
+            assertTrue(dbTuples.next());
+            assertEquals("On iteration " + count, i, dbTuples.getColumnValue(0));
+            assertEquals("On iteration " + count, j, dbTuples.getColumnValue(1));
+            assertEquals("On iteration " + count, k, dbTuples.getColumnValue(2));
+          }
+        }
+      }
+
+      // check that there aren't any extraneous values
+      assertTrue(!dbTuples.next());
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+
+    dbTuples.beforeFirst(new long[] { 1, 133 }, 0);
+    // check that the order is as expected
+    try {
+      long count = 0;
+      int i = 1;
+      int j = 133;
+      for (int k = 1; k < BF_LARGE_LOAD_SIZE * 2; k += 2) {
+        count++;
+        if(!dbTuples.next()) {
+          logger.error("On iteration " + count + " dbTuples failed .next()");
+          assertTrue(false);
+        }
+        assertEquals("i On iteration " + count, i, dbTuples.getColumnValue(0));
+        assertEquals("j On iteration " + count, j, dbTuples.getColumnValue(1));
+        assertEquals("k On iteration " + count, k, dbTuples.getColumnValue(2));
+      }
+
+      // check that there aren't any extraneous values
+      assertTrue(!dbTuples.next());
+    } catch (Throwable e) {
+      // for debugging:
+      // dumpFile(dbTuples);
+      throw e;
+    }
+
+    dbTuples.beforeFirst(new long[] { 3, 4 }, 0);
+    assertFalse(dbTuples.next());
+  }
+
+
+  /**
+   * METHOD TO DO
+   *
+   * @param ft PARAMETER TO DO
+   * @throws Exception EXCEPTION TO DO
+   */
+  @SuppressWarnings("unused")
+  private void dumpFile(DbTuples ft) throws Exception {
+    PrintStream ps = new PrintStream(new FileOutputStream("/tmp/random.dump"));
+
+    try {
+      ft.beforeFirst();
+      while (ft.next()) {
+        ps.println(ft.getColumnValue(0));
+      }
+    } finally {
+      ps.close();
+    }
+  }
+}



More information about the Mulgara-svn mailing list