project 4 skeleton
This commit is contained in:
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
Binary file not shown.
|
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
bin/
|
||||||
|
.DS_Store
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
JDKPATH = /usr
|
||||||
|
LIBPATH = lib/bufmgr.jar:lib/diskmgr.jar:lib/heap.jar:lib/index.jar:lib/junit.jar:lib/org.hamcrest.core_1.3.0.v201303031735.jar
|
||||||
|
SRCPATH = src/main/java/*/
|
||||||
|
TESTPATH = src/test/java/*/
|
||||||
|
|
||||||
|
CLASSPATH = .:..:$(LIBPATH)
|
||||||
|
BINPATH = $(JDKPATH)/bin
|
||||||
|
JAVAC = $(JDKPATH)/bin/javac
|
||||||
|
JAVA = $(JDKPATH)/bin/java
|
||||||
|
|
||||||
|
PROGS = xx
|
||||||
|
|
||||||
|
all: $(PROGS)
|
||||||
|
|
||||||
|
compile:${SRCPATH}*.java
|
||||||
|
$(JAVAC) -cp $(CLASSPATH) -d bin ${SRCPATH}*.java ${TESTPATH}*.java
|
||||||
|
|
||||||
|
xx : compile
|
||||||
|
$(JAVA) -cp $(CLASSPATH):bin tests.ROTest
|
||||||
|
|
||||||
Binary file not shown.
@@ -0,0 +1,131 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>edu.cs.purdue</groupId>
|
||||||
|
<artifactId>cs448p3</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<name>cs448p3</name>
|
||||||
|
<url>https://www.cs.purdue.edu/homes/bb/cs448f19/</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>10</java.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.googlecode.json-simple</groupId>
|
||||||
|
<artifactId>json-simple</artifactId>
|
||||||
|
<version>1.1.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.opencsv</groupId>
|
||||||
|
<artifactId>opencsv</artifactId>
|
||||||
|
<version>4.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>minibase</groupId>
|
||||||
|
<artifactId>bufmgr</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${basedir}/lib/bufmgr.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>minibase</groupId>
|
||||||
|
<artifactId>diskmgr</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${basedir}/lib/diskmgr.jar</systemPath>
|
||||||
|
<type>jar</type>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>minibase</groupId>
|
||||||
|
<artifactId>heap</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${basedir}/lib/heap.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>minibase</groupId>
|
||||||
|
<artifactId>index</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
<scope>system</scope>
|
||||||
|
<systemPath>${basedir}/lib/index.jar</systemPath>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||||
|
<plugins>
|
||||||
|
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
<configuration>
|
||||||
|
<release>${java.version}</release>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.22.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-install-plugin</artifactId>
|
||||||
|
<version>2.5.2</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
|
<version>2.8.2</version>
|
||||||
|
</plugin>
|
||||||
|
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
|
<version>3.7.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>test.TestDriver</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
<descriptorRefs>
|
||||||
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
|
</descriptorRefs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration class for predicate operators.
|
||||||
|
*/
|
||||||
|
public class AttrOperator {
|
||||||
|
|
||||||
|
public static final int EQ = 11;
|
||||||
|
public static final int NEQ = 12;
|
||||||
|
public static final int GT = 13;
|
||||||
|
public static final int GTE = 14;
|
||||||
|
public static final int LT = 15;
|
||||||
|
public static final int LTE = 16;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor (static class).
|
||||||
|
*/
|
||||||
|
private AttrOperator() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the constant value for the string representation (i.e. inverse of
|
||||||
|
* the toString() method).
|
||||||
|
*/
|
||||||
|
public static int toValue(String str) {
|
||||||
|
|
||||||
|
// it may have been better to use the parse tree node, but this
|
||||||
|
// makes it easier to release class projects independently
|
||||||
|
if (str.equals("=")) {
|
||||||
|
return EQ;
|
||||||
|
} else if (str.equals("<>")) {
|
||||||
|
return NEQ;
|
||||||
|
} else if (str.equals(">")) {
|
||||||
|
return GT;
|
||||||
|
} else if (str.equals(">=")) {
|
||||||
|
return GTE;
|
||||||
|
} else if (str.equals("<")) {
|
||||||
|
return LT;
|
||||||
|
} else if (str.equals("<=")) {
|
||||||
|
return LTE;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("unknown operator");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public static int toValue(String str)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of an AttrOperator.
|
||||||
|
*/
|
||||||
|
public static String toString(int value) {
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
|
||||||
|
case EQ:
|
||||||
|
return "=";
|
||||||
|
|
||||||
|
case NEQ:
|
||||||
|
return "<>";
|
||||||
|
|
||||||
|
case GT:
|
||||||
|
return ">";
|
||||||
|
|
||||||
|
case GTE:
|
||||||
|
return ">=";
|
||||||
|
|
||||||
|
case LT:
|
||||||
|
return "<";
|
||||||
|
|
||||||
|
case LTE:
|
||||||
|
return "<=";
|
||||||
|
|
||||||
|
} // switch
|
||||||
|
|
||||||
|
return ("Unexpected AttrOperator " + value);
|
||||||
|
|
||||||
|
} // public static String toString(int value)
|
||||||
|
|
||||||
|
} // public class AttrOperator
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration class for attribute data types.
|
||||||
|
*/
|
||||||
|
public class AttrType {
|
||||||
|
|
||||||
|
public static final int INTEGER = 11;
|
||||||
|
public static final int FLOAT = 12;
|
||||||
|
public static final int STRING = 13;
|
||||||
|
|
||||||
|
public static final int COLNAME = 21;
|
||||||
|
public static final int FIELDNO = 22;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor (static class).
|
||||||
|
*/
|
||||||
|
private AttrType() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AttrType of the given value; i.e. not applicable for column names
|
||||||
|
* or field numbers.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if obj is not an AttrType
|
||||||
|
*/
|
||||||
|
public static int getType(Object obj) {
|
||||||
|
|
||||||
|
if (obj instanceof Integer) {
|
||||||
|
return INTEGER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof Float) {
|
||||||
|
return FLOAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj instanceof String) {
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, unknown type
|
||||||
|
throw new IllegalArgumentException("Unknown AttrType "
|
||||||
|
+ obj.getClass().getName());
|
||||||
|
|
||||||
|
} // public static int getType(Object obj)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of an AttrType.
|
||||||
|
*/
|
||||||
|
public static String toString(int value) {
|
||||||
|
|
||||||
|
switch (value) {
|
||||||
|
|
||||||
|
case INTEGER:
|
||||||
|
return "INTEGER";
|
||||||
|
|
||||||
|
case FLOAT:
|
||||||
|
return "FLOAT";
|
||||||
|
|
||||||
|
case STRING:
|
||||||
|
return "STRING";
|
||||||
|
|
||||||
|
case COLNAME:
|
||||||
|
return "COLNAME";
|
||||||
|
|
||||||
|
case FIELDNO:
|
||||||
|
return "FIELDNO";
|
||||||
|
|
||||||
|
} // switch
|
||||||
|
|
||||||
|
return ("Unexpected AttrType " + value);
|
||||||
|
|
||||||
|
} // public static String toString(int value)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the AttrType that match the given type (@link String)
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int fromString(String type) {
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "INTEGER":
|
||||||
|
return INTEGER;
|
||||||
|
|
||||||
|
case "FLOAT":
|
||||||
|
return FLOAT;
|
||||||
|
|
||||||
|
case "STRING":
|
||||||
|
default:
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public static int fromString (String type)
|
||||||
|
|
||||||
|
} // public class AttrType
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides conversion routines for getting and setting data in byte arrays.
|
||||||
|
*/
|
||||||
|
public class Convert {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from the given byte array at the specified position, and converts it
|
||||||
|
* into a unicode character.
|
||||||
|
*/
|
||||||
|
public static char getCharValue(int pos, byte[] data) {
|
||||||
|
|
||||||
|
// ignoring that UTF-8 could be up to six bytes
|
||||||
|
return (char) data[pos];
|
||||||
|
|
||||||
|
} // public static char getCharValue(int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a unicode character into the given byte array at the specified
|
||||||
|
* position.
|
||||||
|
*/
|
||||||
|
public static void setCharValue(char value, int pos, byte[] data) {
|
||||||
|
|
||||||
|
// ignoring that UTF-8 could be up to six bytes
|
||||||
|
data[pos] = (byte) value;
|
||||||
|
|
||||||
|
} // public static void setCharValue(char value, int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from the given byte array at the specified position, and converts it
|
||||||
|
* into a short.
|
||||||
|
*/
|
||||||
|
public static short getShortValue(int pos, byte[] data) {
|
||||||
|
|
||||||
|
return (short) (((data[pos] & 0xff) << 8) | (data[pos + 1] & 0xff));
|
||||||
|
|
||||||
|
} // public static short getShortValue(int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a short into the given byte array at the specified position.
|
||||||
|
*/
|
||||||
|
public static void setShortValue(short value, int pos, byte[] data) {
|
||||||
|
|
||||||
|
data[pos] = (byte) ((value >> 8) & 0xff);
|
||||||
|
data[pos + 1] = (byte) (value & 0xff);
|
||||||
|
|
||||||
|
} // public static void setShortValue(short value, int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from the given byte array at the specified position, and converts it
|
||||||
|
* into an integer.
|
||||||
|
*/
|
||||||
|
public static int getIntValue(int pos, byte[] data) {
|
||||||
|
|
||||||
|
return ((data[pos] & 0xff) << 24) | ((data[pos + 1] & 0xff) << 16)
|
||||||
|
| ((data[pos + 2] & 0xff) << 8) | (data[pos + 3] & 0xff);
|
||||||
|
|
||||||
|
} // public static int getIntValue(int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an integer into the given byte array at the specified position.
|
||||||
|
*/
|
||||||
|
public static void setIntValue(int value, int pos, byte[] data) {
|
||||||
|
|
||||||
|
data[pos] = (byte) ((value >> 24) & 0xff);
|
||||||
|
data[pos + 1] = (byte) ((value >> 16) & 0xff);
|
||||||
|
data[pos + 2] = (byte) ((value >> 8) & 0xff);
|
||||||
|
data[pos + 3] = (byte) (value & 0xff);
|
||||||
|
|
||||||
|
} // public static void setIntValue(int value, int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from the given byte array at the specified position, and converts it
|
||||||
|
* to a float.
|
||||||
|
*/
|
||||||
|
public static float getFloatValue(int pos, byte[] data) {
|
||||||
|
|
||||||
|
// let java do the IEEE 754 conversion
|
||||||
|
return Float.intBitsToFloat(getIntValue(pos, data));
|
||||||
|
|
||||||
|
} // public static float getFloatValue(int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a float into the given byte array at the specified position.
|
||||||
|
*/
|
||||||
|
public static void setFloatValue(float value, int pos, byte[] data) {
|
||||||
|
|
||||||
|
// let java do the IEEE 754 conversion
|
||||||
|
setIntValue(Float.floatToIntBits(value), pos, data);
|
||||||
|
|
||||||
|
} // public static void setFloatValue(float value, int pos, byte[] data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads from the given byte array at the specified position, and converts it
|
||||||
|
* to a string of given length.
|
||||||
|
*/
|
||||||
|
public static String getStringValue(int pos, byte[] data, int length) {
|
||||||
|
|
||||||
|
// validate the maximum length
|
||||||
|
int buflen = data.length - pos;
|
||||||
|
if (buflen < length) {
|
||||||
|
length = buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// is there any way in Java to avoid these mem copies?
|
||||||
|
return new String(data, pos, length).trim();
|
||||||
|
|
||||||
|
} // public static String getStringValue(int pos, byte[] data, int length)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a string into the given byte array at the specified position.
|
||||||
|
*/
|
||||||
|
public static void setStringValue(String value, int pos, byte[] data) {
|
||||||
|
|
||||||
|
// is there any way in Java to avoid these mem copies?
|
||||||
|
byte[] ba = value.getBytes();
|
||||||
|
System.arraycopy(ba, 0, data, pos, ba.length);
|
||||||
|
|
||||||
|
} // public static void setStringValue(String value, int pos, byte[] data)
|
||||||
|
|
||||||
|
} // public class Convert
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global constants; implement this interface to access them conveniently.
|
||||||
|
*/
|
||||||
|
public interface GlobalConst {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Disk Manager Constants
|
||||||
|
//
|
||||||
|
|
||||||
|
/** Size of a page, in bytes. */
|
||||||
|
public static final int PAGE_SIZE = 1024;
|
||||||
|
|
||||||
|
/** Page number of an invalid page (i.e. null pointer). */
|
||||||
|
public static final int INVALID_PAGEID = -1;
|
||||||
|
|
||||||
|
/** Page number of the first page in a database file. */
|
||||||
|
public static final int FIRST_PAGEID = 0;
|
||||||
|
|
||||||
|
/** Maximum size of a name (i.e. of files or attributes). */
|
||||||
|
public static final int NAME_MAXLEN = 50;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Buffer Manager Constants
|
||||||
|
//
|
||||||
|
|
||||||
|
/** Replace the buffer frame with an existing memory page. */
|
||||||
|
public static final boolean PIN_MEMCPY = true;
|
||||||
|
|
||||||
|
/** Replace an existing memory page with the current disk page. */
|
||||||
|
public static final boolean PIN_DISKIO = false;
|
||||||
|
|
||||||
|
/** Forces the page to be written to disk when unpinned. */
|
||||||
|
public static final boolean UNPIN_DIRTY = true;
|
||||||
|
|
||||||
|
/** Optimization to avoid writing to disk when unpinned. */
|
||||||
|
public static final boolean UNPIN_CLEAN = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Heap File Constants
|
||||||
|
//
|
||||||
|
|
||||||
|
/** Length of an "empty" slot in a heap file page. */
|
||||||
|
public static final int EMPTY_SLOT = -1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// System Catalog Constants
|
||||||
|
//
|
||||||
|
|
||||||
|
/** Maximum length of a column (in bytes). */
|
||||||
|
public static final int MAX_COLSIZE = 1001;
|
||||||
|
|
||||||
|
/** Maximum length of a tuple (in bytes). */
|
||||||
|
public static final int MAX_TUPSIZE = 1004;
|
||||||
|
|
||||||
|
} // public interface GlobalConst
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
import bufmgr.BufMgr;
|
||||||
|
import diskmgr.DiskMgr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definitions for the running Minibase system, including references to static
|
||||||
|
* layers and database-level attributes.
|
||||||
|
*/
|
||||||
|
public class Minibase {
|
||||||
|
|
||||||
|
/** Name of the data file. */
|
||||||
|
public static String DatabaseName;
|
||||||
|
|
||||||
|
/** The Minibase Disk Space Manager. */
|
||||||
|
public static DiskMgr DiskManager;
|
||||||
|
|
||||||
|
/** The Minibase Buffer Manager. */
|
||||||
|
public static BufMgr BufferManager;
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs and starts an instance of Minibase, given the configuration.
|
||||||
|
*
|
||||||
|
* @param dbname Name of the data file
|
||||||
|
* @param num_pgs Number of pages to allocate
|
||||||
|
* @param bufpoolsize Buffer pool size (in pages)
|
||||||
|
* @param replacement_policy Buffer pool replacement policy
|
||||||
|
* @param exists If the database already exists on disk
|
||||||
|
*/
|
||||||
|
public Minibase(String dbname, int num_pgs, int bufpoolsize,
|
||||||
|
String replacement_policy, boolean exists) {
|
||||||
|
|
||||||
|
// simply initialize the database
|
||||||
|
init(dbname, num_pgs, bufpoolsize, replacement_policy, exists);
|
||||||
|
|
||||||
|
} // constructor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the current instance of Minibase with the given configuration.
|
||||||
|
*
|
||||||
|
* @param dbname Name of the data file
|
||||||
|
* @param num_pgs Number of pages to allocate
|
||||||
|
* @param bufpoolsize Buffer pool size (in pages)
|
||||||
|
* @param replacement_policy Buffer pool replacement policy
|
||||||
|
* @param exists If the database already exists on disk
|
||||||
|
*/
|
||||||
|
public void init(String dbname, int num_pgs, int bufpoolsize,
|
||||||
|
String replacement_policy, boolean exists) {
|
||||||
|
|
||||||
|
// save the file name
|
||||||
|
DatabaseName = dbname;
|
||||||
|
|
||||||
|
// load the static layers
|
||||||
|
try {
|
||||||
|
DiskManager = new DiskMgr();
|
||||||
|
BufferManager = new BufMgr(bufpoolsize);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
haltSystem(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create or open the database
|
||||||
|
try {
|
||||||
|
if (exists) {
|
||||||
|
DiskManager.openDB(dbname);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DiskManager.createDB(dbname, num_pgs);
|
||||||
|
|
||||||
|
BufferManager.flushAllPages();
|
||||||
|
}
|
||||||
|
} catch (Exception exc) {
|
||||||
|
haltSystem(exc);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // init
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an unrecoverable error and halts the system.
|
||||||
|
*/
|
||||||
|
public static void haltSystem(Exception exc) {
|
||||||
|
System.err.println("\n*** Unrecoverable system error ***");
|
||||||
|
exc.printStackTrace();
|
||||||
|
Runtime.getRuntime().exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class Minibase
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lowest-level view of a disk page.
|
||||||
|
*/
|
||||||
|
public class Page implements GlobalConst {
|
||||||
|
|
||||||
|
/** The actual byte array for the page. */
|
||||||
|
protected byte[] data;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor; creates a blank page.
|
||||||
|
*/
|
||||||
|
public Page() {
|
||||||
|
data = new byte[PAGE_SIZE];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor that wraps the given byte array.
|
||||||
|
*/
|
||||||
|
public Page(byte[] data) {
|
||||||
|
setData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get accessor for the data byte array.
|
||||||
|
*/
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set accessor for the data byte array.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the data array size is invalid
|
||||||
|
*/
|
||||||
|
public void setData(byte[] data) {
|
||||||
|
if (data.length != PAGE_SIZE) {
|
||||||
|
Minibase.haltSystem(new IllegalArgumentException(
|
||||||
|
"Invalid page buffer size"));
|
||||||
|
}
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this page's data array to share the given page's data array.
|
||||||
|
*/
|
||||||
|
public void setPage(Page page) {
|
||||||
|
this.data = page.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the contents of the given page's buffer into this page's buffer.
|
||||||
|
*/
|
||||||
|
public void copyPage(Page page) {
|
||||||
|
System.arraycopy(page.data, 0, this.data, 0, PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a char at the given page offset.
|
||||||
|
*/
|
||||||
|
public char getCharValue(int offset) {
|
||||||
|
return Convert.getCharValue(offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a char at the given page offset.
|
||||||
|
*/
|
||||||
|
public void setCharValue(char value, int offset) {
|
||||||
|
Convert.setCharValue(value, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a short at the given page offset.
|
||||||
|
*/
|
||||||
|
public short getShortValue(int offset) {
|
||||||
|
return Convert.getShortValue(offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a short at the given page offset.
|
||||||
|
*/
|
||||||
|
public void setShortValue(short value, int offset) {
|
||||||
|
Convert.setShortValue(value, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an int at the given page offset.
|
||||||
|
*/
|
||||||
|
public int getIntValue(int offset) {
|
||||||
|
return Convert.getIntValue(offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an int at the given page offset.
|
||||||
|
*/
|
||||||
|
public void setIntValue(int value, int offset) {
|
||||||
|
Convert.setIntValue(value, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a float at the given page offset.
|
||||||
|
*/
|
||||||
|
public float getFloatValue(int offset) {
|
||||||
|
return Convert.getFloatValue(offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a float at the given page offset.
|
||||||
|
*/
|
||||||
|
public void setFloatValue(float value, int offset) {
|
||||||
|
Convert.setFloatValue(value, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a string at the given page offset, given the maximum length.
|
||||||
|
*/
|
||||||
|
public String getStringValue(int offset, int length) {
|
||||||
|
return Convert.getStringValue(offset, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a string at the given page offset.
|
||||||
|
*/
|
||||||
|
public void setStringValue(String value, int offset) {
|
||||||
|
Convert.setStringValue(value, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class Page implements GlobalConst
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Although a PageId is simply an integer, wrapping it provides convenient
|
||||||
|
* methods and allows returning it via method parameters.
|
||||||
|
*/
|
||||||
|
public class PageId implements GlobalConst {
|
||||||
|
|
||||||
|
/** The actual page id value. */
|
||||||
|
public int pid;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
public PageId() {
|
||||||
|
pid = INVALID_PAGEID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a PageId from the given value.
|
||||||
|
*/
|
||||||
|
public PageId(int pid) {
|
||||||
|
this.pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the given PageId.
|
||||||
|
*/
|
||||||
|
public void copyPageId(PageId pageNo) {
|
||||||
|
this.pid = pageNo.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code value for the PageId.
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if obj is a PageId with the same pid; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof PageId) {
|
||||||
|
PageId pageno = (PageId) obj;
|
||||||
|
return (this.pid == pageno.pid);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the PageId.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return Integer.toString(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class PageId implements GlobalConst
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A record is uniquely identified by its page number and slot number.
|
||||||
|
*/
|
||||||
|
public class RID {
|
||||||
|
|
||||||
|
/** The record's page number. */
|
||||||
|
public PageId pageno;
|
||||||
|
|
||||||
|
/** The record's slot number. */
|
||||||
|
public int slotno;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
public RID() {
|
||||||
|
pageno = new PageId();
|
||||||
|
slotno = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor.
|
||||||
|
*/
|
||||||
|
public RID(RID rid) {
|
||||||
|
copyRID(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an RID from the given values.
|
||||||
|
*/
|
||||||
|
public RID(PageId pageno, int slotno) {
|
||||||
|
this.pageno = new PageId();
|
||||||
|
this.pageno.pid = pageno.pid;
|
||||||
|
this.slotno = slotno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an RID stored in the given data buffer.
|
||||||
|
*/
|
||||||
|
public RID(byte[] data, short offset) {
|
||||||
|
pageno = new PageId(Convert.getIntValue(offset, data));
|
||||||
|
slotno = Convert.getIntValue(offset + 4, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the RID into the given data buffer.
|
||||||
|
*/
|
||||||
|
public void writeData(byte[] data, short offset) {
|
||||||
|
Convert.setIntValue(pageno.pid, offset, data);
|
||||||
|
Convert.setIntValue(slotno, offset + 4, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total length of the RID (in bytes).
|
||||||
|
*/
|
||||||
|
public short getLength() {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a copy of the given RID.
|
||||||
|
*/
|
||||||
|
public void copyRID(RID rid) {
|
||||||
|
this.pageno = new PageId();
|
||||||
|
this.pageno.pid = rid.pageno.pid;
|
||||||
|
slotno = rid.slotno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code value for the RID.
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
// won't work if DB supports more than 65535 pages
|
||||||
|
return ((pageno.pid & 0xFFFF) << 16) | (slotno & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if obj is an RID with the same values; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof RID) {
|
||||||
|
RID rid = (RID) obj;
|
||||||
|
return ((pageno.pid == rid.pageno.pid) && (slotno == rid.slotno));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the PageId.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return pageno.toString() + ":" + Integer.toString(slotno);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class RID
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a general and type-safe way to store and compare index search keys.
|
||||||
|
*/
|
||||||
|
public class SearchKey implements Comparable<SearchKey> {
|
||||||
|
|
||||||
|
/** Internal type number for Integer keys. */
|
||||||
|
protected static final byte INTEGER_KEY = 1;
|
||||||
|
|
||||||
|
/** Internal type number for Float keys. */
|
||||||
|
protected static final byte FLOAT_KEY = 2;
|
||||||
|
|
||||||
|
/** Internal type number for String keys. */
|
||||||
|
protected static final byte STRING_KEY = 3;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** The type of the key value. */
|
||||||
|
protected byte type;
|
||||||
|
|
||||||
|
/** The size of the key value (in bytes). */
|
||||||
|
protected short size;
|
||||||
|
|
||||||
|
/** The actual key value. */
|
||||||
|
protected Object value;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new integer search key.
|
||||||
|
*/
|
||||||
|
public SearchKey(Integer value) {
|
||||||
|
this.type = INTEGER_KEY;
|
||||||
|
this.size = Integer.SIZE / Byte.SIZE;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new float search key.
|
||||||
|
*/
|
||||||
|
public SearchKey(Float value) {
|
||||||
|
this.type = FLOAT_KEY;
|
||||||
|
this.size = Float.SIZE / Byte.SIZE;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new string search key.
|
||||||
|
*/
|
||||||
|
public SearchKey(String value) {
|
||||||
|
this.type = STRING_KEY;
|
||||||
|
this.size = (short) value.length();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a search key from a generic value.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if value's type is invalid
|
||||||
|
*/
|
||||||
|
public SearchKey(Object value) {
|
||||||
|
|
||||||
|
// Integer key?
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
this.type = INTEGER_KEY;
|
||||||
|
this.size = Integer.SIZE / Byte.SIZE;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float key?
|
||||||
|
else if (value instanceof Float) {
|
||||||
|
this.type = FLOAT_KEY;
|
||||||
|
this.size = Float.SIZE / Byte.SIZE;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String key?
|
||||||
|
else if (value instanceof String) {
|
||||||
|
this.type = STRING_KEY;
|
||||||
|
this.size = (short) ((String) value).length();
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, none of the above
|
||||||
|
else {
|
||||||
|
throw new IllegalArgumentException("invalid key value type");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public SearchKey(Object value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy constructor for a search key.
|
||||||
|
*/
|
||||||
|
public SearchKey(SearchKey key) {
|
||||||
|
|
||||||
|
// copy the type and size
|
||||||
|
this.type = key.type;
|
||||||
|
this.size = key.size;
|
||||||
|
|
||||||
|
// copy the value
|
||||||
|
switch (key.type) {
|
||||||
|
case INTEGER_KEY:
|
||||||
|
this.value = new Integer((Integer) key.value);
|
||||||
|
break;
|
||||||
|
case FLOAT_KEY:
|
||||||
|
this.value = new Float((Float) key.value);
|
||||||
|
break;
|
||||||
|
case STRING_KEY:
|
||||||
|
this.value = new String((String) key.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public SearchKey(SearchKey key)
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a SearchKey stored in the given data buffer.
|
||||||
|
*/
|
||||||
|
public SearchKey(byte[] data, short offset) {
|
||||||
|
|
||||||
|
// extract the type and size
|
||||||
|
type = data[offset];
|
||||||
|
size = Convert.getShortValue(offset + 1, data);
|
||||||
|
|
||||||
|
// extract the key value
|
||||||
|
switch (type) {
|
||||||
|
case INTEGER_KEY:
|
||||||
|
value = new Integer(Convert.getIntValue(offset + 3, data));
|
||||||
|
break;
|
||||||
|
case FLOAT_KEY:
|
||||||
|
value = new Float(Convert.getFloatValue(offset + 3, data));
|
||||||
|
break;
|
||||||
|
case STRING_KEY:
|
||||||
|
value = Convert.getStringValue(offset + 3, data, size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public SearchKey(byte[] data, short offset)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the SearchKey into the given data buffer.
|
||||||
|
*/
|
||||||
|
public void writeData(byte[] data, short offset) {
|
||||||
|
|
||||||
|
// write the type and size
|
||||||
|
data[offset] = type;
|
||||||
|
Convert.setShortValue(size, offset + 1, data);
|
||||||
|
|
||||||
|
// write the key value
|
||||||
|
switch (type) {
|
||||||
|
case INTEGER_KEY:
|
||||||
|
Convert.setIntValue((Integer) value, offset + 3, data);
|
||||||
|
break;
|
||||||
|
case FLOAT_KEY:
|
||||||
|
Convert.setFloatValue((Float) value, offset + 3, data);
|
||||||
|
break;
|
||||||
|
case STRING_KEY:
|
||||||
|
Convert.setStringValue((String) value, offset + 3, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public void writeData(byte[] data, short offset)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total length of the search key (in bytes).
|
||||||
|
*/
|
||||||
|
public short getLength() {
|
||||||
|
return (short) (3 + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the hash value for the search key, given the depth (i.e. number of
|
||||||
|
* bits to consider).
|
||||||
|
*/
|
||||||
|
public int getHash(int depth) {
|
||||||
|
|
||||||
|
// apply the appropriate calculation
|
||||||
|
int mask = (1 << depth) - 1;
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
default:
|
||||||
|
case INTEGER_KEY:
|
||||||
|
int ikey = ((Integer) value).intValue();
|
||||||
|
return ikey & mask;
|
||||||
|
|
||||||
|
case FLOAT_KEY:
|
||||||
|
int fkey = Float.floatToIntBits((Float) value);
|
||||||
|
return fkey & mask;
|
||||||
|
|
||||||
|
case STRING_KEY:
|
||||||
|
|
||||||
|
// reverse the first four bytes of the string
|
||||||
|
byte[] s = ((String) value).getBytes();
|
||||||
|
int skey = 0;
|
||||||
|
int len = s.length > 4 ? 4 : s.length;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
skey |= (s[i] << (i * Byte.SIZE));
|
||||||
|
}
|
||||||
|
return skey & mask;
|
||||||
|
|
||||||
|
} // switch
|
||||||
|
|
||||||
|
} // public int getHash(int depth)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the search key matches the given hash value, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isHash(int hash) {
|
||||||
|
|
||||||
|
// calculate the bit depth (i.e. the left-most '1' bit)
|
||||||
|
int depth = (int) (Math.log(hash) / Math.log(2) + 1);
|
||||||
|
|
||||||
|
// compare the hash codes
|
||||||
|
return (getHash(depth) == hash);
|
||||||
|
|
||||||
|
} // public boolean isHash(int hash)
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a generic hash code for the key value.
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
return value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if obj is a SearchKey with the same values; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof SearchKey) {
|
||||||
|
SearchKey key = (SearchKey) obj;
|
||||||
|
return (value.equals(key.value));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generically compares two search keys.
|
||||||
|
*
|
||||||
|
* @return a negative integer, zero, or a positive integer as this object is
|
||||||
|
* less than, equal to, or greater than the specified object
|
||||||
|
* @throws IllegalArgumentException if the search keys are not comparable
|
||||||
|
*/
|
||||||
|
public int compareTo(SearchKey key) {
|
||||||
|
|
||||||
|
// Integer comparison
|
||||||
|
if (value instanceof Integer) {
|
||||||
|
if (key.value instanceof Integer) {
|
||||||
|
|
||||||
|
Integer ikey1 = (Integer) this.value;
|
||||||
|
Integer ikey2 = (Integer) key.value;
|
||||||
|
return ikey1.compareTo(ikey2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("search keys are not comparable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float comparison
|
||||||
|
if (value instanceof Float) {
|
||||||
|
if (key.value instanceof Float) {
|
||||||
|
|
||||||
|
Float fkey1 = (Float) this.value;
|
||||||
|
Float fkey2 = (Float) key.value;
|
||||||
|
return fkey1.compareTo(fkey2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("search keys are not comparable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// default: String comparison
|
||||||
|
if (key.value instanceof String) {
|
||||||
|
|
||||||
|
String skey1 = (String) this.value;
|
||||||
|
String skey2 = (String) key.value;
|
||||||
|
return skey1.compareTo(skey2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("search keys are not comparable");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public int compareTo(SearchKey key)
|
||||||
|
|
||||||
|
} // public class SearchKey implements Comparable<SearchKey>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One term in an ORDER BY clause; i.e. relations can be sorted on multiple
|
||||||
|
* (distinct) attributes, as well as in ascending or descending order.
|
||||||
|
*/
|
||||||
|
public class SortKey {
|
||||||
|
|
||||||
|
/** Column name or field number. */
|
||||||
|
public Object field;
|
||||||
|
|
||||||
|
/** True if descending order, false if ascending order. */
|
||||||
|
public boolean isDesc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a SortKey from the given values.
|
||||||
|
*/
|
||||||
|
public SortKey(Object field, boolean isDesc) {
|
||||||
|
this.field = field;
|
||||||
|
this.isDesc = isDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class SortKey
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.RID;
|
||||||
|
import heap.HeapFile;
|
||||||
|
import heap.HeapScan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for heap file scan, the most basic access method. This "iterator"
|
||||||
|
* version takes schema into consideration and generates real tuples.
|
||||||
|
*/
|
||||||
|
public class FileScan extends Iterator {
|
||||||
|
|
||||||
|
private HeapFile file = null; // needed for restart(), getFile();
|
||||||
|
private HeapScan scan = null;
|
||||||
|
private RID rid = null;
|
||||||
|
private boolean isOpen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a file scan, given the schema and heap file.
|
||||||
|
*/
|
||||||
|
public FileScan(Schema aSchema, HeapFile aFile) {
|
||||||
|
this.schema = aSchema;
|
||||||
|
this.file = aFile;
|
||||||
|
this.scan = file.openScan();
|
||||||
|
this.rid = new RID();
|
||||||
|
isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
for(int i = 0; i < depth; i++){
|
||||||
|
System.out.printf(" ");
|
||||||
|
}
|
||||||
|
System.out.printf("FileScan\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
scan.close();
|
||||||
|
scan = file.openScan();
|
||||||
|
//rid = new RID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
scan.close();
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
return scan.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
return new Tuple(schema, scan.getNext(rid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the RID of the last tuple returned.
|
||||||
|
*/
|
||||||
|
public RID getLastRID() {
|
||||||
|
return rid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getter; added so HashJoin doesn't have to copy the file;
|
||||||
|
public HeapFile getFile(){
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class FileScan extends Iterator
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import heap.HeapFile;
|
||||||
|
import index.HashIndex;
|
||||||
|
import global.SearchKey;
|
||||||
|
import global.RID;
|
||||||
|
import global.AttrOperator;
|
||||||
|
import global.AttrType;
|
||||||
|
|
||||||
|
public class HashJoin extends Iterator {
|
||||||
|
|
||||||
|
public HashJoin(Iterator aIter1, Iterator aIter2, int aJoinCol1, int aJoinCol2){
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void explain(int depth) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restart() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOpen() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tuple getNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
} // end class HashJoin;
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.SearchKey;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A extension to Java's hash table that allows duplicate keys.
|
||||||
|
* http://mindprod.com/jgloss/hashtable.html
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
class HashTableDup extends Hashtable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the specified key to the specified value in this hashtable.
|
||||||
|
*/
|
||||||
|
public void add(SearchKey key, Tuple value) {
|
||||||
|
|
||||||
|
// to conserve RAM, duplicate values are either stored as single objects,
|
||||||
|
// pairs in an array, or multiples in an ArrayList
|
||||||
|
Object existing = get(key);
|
||||||
|
if (existing == null) {
|
||||||
|
|
||||||
|
// store the single value
|
||||||
|
put(key, value);
|
||||||
|
|
||||||
|
} else if (existing instanceof Tuple) {
|
||||||
|
|
||||||
|
// was a single object; make into a pair
|
||||||
|
put(key, new Tuple[] { (Tuple) existing, value });
|
||||||
|
|
||||||
|
} else if (existing instanceof Tuple[]) {
|
||||||
|
|
||||||
|
// was a pair; make into an ArrayList of 3
|
||||||
|
ArrayList<Tuple> a = new ArrayList<Tuple>();
|
||||||
|
a.addAll(Arrays.asList((Tuple[]) existing));
|
||||||
|
a.add(value);
|
||||||
|
put(key, a);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// just add to tail end of existing ArrayList
|
||||||
|
((ArrayList<Tuple>) existing).add(value);
|
||||||
|
|
||||||
|
} // else
|
||||||
|
|
||||||
|
} // public void add(SearchKey key, Tuple value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the values to which the specified key is mapped in this hashtable.
|
||||||
|
*/
|
||||||
|
public Tuple[] getAll(SearchKey key) {
|
||||||
|
|
||||||
|
// look up the key
|
||||||
|
Object match = get(key);
|
||||||
|
if (match == null) {
|
||||||
|
|
||||||
|
// not found
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} else if (match instanceof Tuple) {
|
||||||
|
|
||||||
|
// return the single match
|
||||||
|
return new Tuple[] { (Tuple) match };
|
||||||
|
|
||||||
|
} else if (match instanceof Tuple[]) {
|
||||||
|
|
||||||
|
// return the matches
|
||||||
|
return (Tuple[]) match;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// convert ArrayList to Tuple[]
|
||||||
|
ArrayList<Tuple> a = (ArrayList<Tuple>) match;
|
||||||
|
return a.toArray(new Tuple[a.size()]);
|
||||||
|
|
||||||
|
} // elsee
|
||||||
|
|
||||||
|
} // public Tuple[] getAll(SearchKey key)
|
||||||
|
|
||||||
|
} // class HashTableDup extends Hashtable
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.SearchKey;
|
||||||
|
import heap.HeapFile;
|
||||||
|
import index.HashIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for bucket scan, an index access method.
|
||||||
|
*/
|
||||||
|
public class IndexScan extends Iterator {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an index scan, given the hash index and schema.
|
||||||
|
*/
|
||||||
|
public IndexScan(Schema schema, HashIndex index, HeapFile file) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explaination of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the key of the last tuple returned.
|
||||||
|
*/
|
||||||
|
public SearchKey getLastKey() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hash value for the bucket containing the next tuple, or maximum
|
||||||
|
* number of buckets if none.
|
||||||
|
*/
|
||||||
|
public int getNextHash() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class IndexScan extends Iterator
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minibase Iterators
|
||||||
|
* Query execution is driven by a tree of relational operators, all of which are
|
||||||
|
* implemented as iterators. Results are requested by successive "get next
|
||||||
|
* tuple" calls on the root of the tree, which in turn makes similar calls to
|
||||||
|
* child iterators throughout the tree. Intermediate nodes (i.e. join iterators)
|
||||||
|
* drive the leaf-level nodes (i.e. file or index scan iterators).
|
||||||
|
*/
|
||||||
|
public abstract class Iterator {
|
||||||
|
|
||||||
|
/** Schema for resulting tuples; must be set in all subclass constructors. */
|
||||||
|
protected Schema schema;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public abstract void explain(int depth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs the indentation for the given depth.
|
||||||
|
*/
|
||||||
|
protected void indent(int depth) {
|
||||||
|
for (int i = 0; i < depth * 2; i++) {
|
||||||
|
System.out.print(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public abstract void restart();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public abstract boolean isOpen();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public abstract void close();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public abstract boolean hasNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public abstract Tuple getNext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the schema, gets and prints all tuples, and closes the iterator.
|
||||||
|
*
|
||||||
|
* @return number of tuples processed
|
||||||
|
*/
|
||||||
|
public int execute() {
|
||||||
|
result = ""; //Modification to store result of queries for test case comparison
|
||||||
|
int cnt = 0;
|
||||||
|
getSchema().print();
|
||||||
|
Tuple next;
|
||||||
|
while (hasNext()) {
|
||||||
|
next = getNext();
|
||||||
|
next.print();
|
||||||
|
//Modification to store result of queries for test case comparison
|
||||||
|
result += next.toString() + "|";
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
if(result.length() > 0)
|
||||||
|
result = result.substring(0, result.length() - 1);
|
||||||
|
close();
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the garbage collector when there are no more references to the
|
||||||
|
* object; closes the iterator if it's still open.
|
||||||
|
*/
|
||||||
|
protected void finalize() throws Throwable {
|
||||||
|
if (isOpen()) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchema(Schema schema) {
|
||||||
|
this.schema = schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Schema getSchema() {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
// *** DO NOT CHANGE ANY EXISTING METHODS OR VARIABLES BELOW THIS LINE ***
|
||||||
|
//Store values for test cases only
|
||||||
|
//String format: row0|row1|row2|...rowN|
|
||||||
|
private String result;
|
||||||
|
public String getResult() { return result; }
|
||||||
|
|
||||||
|
} // public abstract class Iterator
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.SearchKey;
|
||||||
|
import heap.HeapFile;
|
||||||
|
import index.HashIndex;
|
||||||
|
import index.HashScan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for hash scan, an index access method.
|
||||||
|
*/
|
||||||
|
public class KeyScan extends Iterator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an index scan, given the hash index and schema.
|
||||||
|
*/
|
||||||
|
public KeyScan(Schema aSchema, HashIndex aIndex, SearchKey aKey, HeapFile aFile) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class KeyScan extends Iterator
|
||||||
@@ -0,0 +1,188 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.AttrOperator;
|
||||||
|
import global.AttrType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal representation of simple SQL expressions. Operators are referenced
|
||||||
|
* by constants in global.AttrOperator, and operands can be constants or field
|
||||||
|
* numbers (i.e. if referring to an attribute).
|
||||||
|
*/
|
||||||
|
public class Predicate {
|
||||||
|
|
||||||
|
/** The operator. */
|
||||||
|
protected int oper;
|
||||||
|
|
||||||
|
/** Type of left operand. */
|
||||||
|
protected int ltype;
|
||||||
|
|
||||||
|
/** Left operand. */
|
||||||
|
protected Object left;
|
||||||
|
|
||||||
|
/** Type of right operand. */
|
||||||
|
protected int rtype;
|
||||||
|
|
||||||
|
/** Right operand. */
|
||||||
|
protected Object right;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a predicate, given the expression.
|
||||||
|
*
|
||||||
|
* @param oper AttrOperator constant
|
||||||
|
* @param ltype AttrType of the field
|
||||||
|
* @param left operand value, field number, or column name
|
||||||
|
* @param rtype AttrType of the field
|
||||||
|
* @param right operand value, field number, or column name
|
||||||
|
*/
|
||||||
|
public Predicate(int oper, int ltype, Object left, int rtype, Object right) {
|
||||||
|
this.oper = oper;
|
||||||
|
this.ltype = ltype;
|
||||||
|
this.rtype = rtype;
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates AttrType.COLNAME operands against the given schema.
|
||||||
|
*
|
||||||
|
* @return true if the predicate is valid, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean validate(Schema schema) {
|
||||||
|
|
||||||
|
// if column name, make sure it exists
|
||||||
|
int type1 = ltype;
|
||||||
|
if (type1 == AttrType.COLNAME) {
|
||||||
|
int fldno = schema.fieldNumber((String) left);
|
||||||
|
if (fldno < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
type1 = schema.fieldType(fldno);
|
||||||
|
}
|
||||||
|
int type2 = rtype;
|
||||||
|
if (type2 == AttrType.COLNAME) {
|
||||||
|
int fldno = schema.fieldNumber((String) right);
|
||||||
|
if (fldno < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
type2 = schema.fieldType(fldno);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test left and right type compatibility
|
||||||
|
return (type1 == type2);
|
||||||
|
|
||||||
|
} // public boolean validate(Schema schema)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the predicate on the given tuple and returns true if it passes.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if member data lead to an invalid operation
|
||||||
|
*/
|
||||||
|
public boolean evaluate(Tuple tuple) {
|
||||||
|
|
||||||
|
// if necessary, resolve column names to field numbers
|
||||||
|
if (ltype == AttrType.COLNAME) {
|
||||||
|
left = tuple.schema.fieldNumber((String) left);
|
||||||
|
ltype = AttrType.FIELDNO;
|
||||||
|
}
|
||||||
|
if (rtype == AttrType.COLNAME) {
|
||||||
|
right = tuple.schema.fieldNumber((String) right);
|
||||||
|
rtype = AttrType.FIELDNO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the values to compare
|
||||||
|
int type = ltype;
|
||||||
|
Object lval = left;
|
||||||
|
if (ltype == AttrType.FIELDNO) {
|
||||||
|
type = tuple.schema.fieldType((Integer) lval);
|
||||||
|
lval = tuple.getField((Integer) lval);
|
||||||
|
}
|
||||||
|
Object rval = right;
|
||||||
|
if (rtype == AttrType.FIELDNO) {
|
||||||
|
rval = tuple.getField((Integer) rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare the values
|
||||||
|
int comp = 0;
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case AttrType.INTEGER:
|
||||||
|
comp = ((Integer) lval).compareTo((Integer) rval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.FLOAT:
|
||||||
|
comp = ((Float) lval).compareTo((Float) rval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.STRING:
|
||||||
|
comp = ((String) lval).compareTo((String) rval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("unknown types to compare");
|
||||||
|
|
||||||
|
} // switch (type)
|
||||||
|
|
||||||
|
// evaluate the operator
|
||||||
|
switch (oper) {
|
||||||
|
|
||||||
|
case AttrOperator.EQ:
|
||||||
|
return (comp == 0);
|
||||||
|
|
||||||
|
case AttrOperator.NEQ:
|
||||||
|
return (comp != 0);
|
||||||
|
|
||||||
|
case AttrOperator.GT:
|
||||||
|
return (comp > 0);
|
||||||
|
|
||||||
|
case AttrOperator.GTE:
|
||||||
|
return (comp >= 0);
|
||||||
|
|
||||||
|
case AttrOperator.LT:
|
||||||
|
return (comp < 0);
|
||||||
|
|
||||||
|
case AttrOperator.LTE:
|
||||||
|
return (comp <= 0);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("unknown operator to evaluate");
|
||||||
|
|
||||||
|
} // switch (oper)
|
||||||
|
|
||||||
|
} // public boolean evaluate(Tuple tuple)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the Predicate.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
|
||||||
|
return opString(ltype, left) + ' ' + AttrOperator.toString(oper) + ' '
|
||||||
|
+ opString(rtype, right);
|
||||||
|
|
||||||
|
} // public String toString()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of an operand.
|
||||||
|
*/
|
||||||
|
protected String opString(int type, Object operand) {
|
||||||
|
|
||||||
|
// get the string version
|
||||||
|
String str = operand.toString();
|
||||||
|
|
||||||
|
// wrap strings in single quotes
|
||||||
|
if (type == AttrType.STRING) {
|
||||||
|
return "'" + str + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap column numbers in braces
|
||||||
|
if (type == AttrType.FIELDNO) {
|
||||||
|
return "{" + str + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, just return the string
|
||||||
|
return str;
|
||||||
|
|
||||||
|
} // protected String opString(int type, Object obj)
|
||||||
|
|
||||||
|
} // public class Predicate
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The projection operator extracts columns from a relation; unlike in
|
||||||
|
* relational algebra, this operator does NOT eliminate duplicate tuples.
|
||||||
|
*/
|
||||||
|
public class Projection extends Iterator {
|
||||||
|
|
||||||
|
private Iterator iter = null;
|
||||||
|
private Integer[] fields = null;
|
||||||
|
private boolean isOpen;
|
||||||
|
/**
|
||||||
|
* Constructs a projection, given the underlying iterator and field numbers.
|
||||||
|
*/
|
||||||
|
public Projection(Iterator aIter, Integer... aFields) {
|
||||||
|
this.iter = aIter;
|
||||||
|
this.fields = aFields;
|
||||||
|
this.isOpen = true;
|
||||||
|
|
||||||
|
this.schema = new Schema(fields.length);
|
||||||
|
Schema iterSchema = iter.getSchema();
|
||||||
|
for(int i = 0; i < fields.length; i++){
|
||||||
|
this.schema.initField(i, iterSchema, fields[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class Projection extends Iterator
|
||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.AttrType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each tuple has a schema that defines the logical view of the raw bytes; it
|
||||||
|
* describes the types, lengths, offsets, and names of a tuple's fields.
|
||||||
|
*/
|
||||||
|
public class Schema {
|
||||||
|
|
||||||
|
/** Minimum column width for output. */
|
||||||
|
public static final int MIN_WIDTH = 10;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Attribute types of the fields. */
|
||||||
|
protected int[] types;
|
||||||
|
|
||||||
|
/** Variable lengths of the fields. */
|
||||||
|
protected int[] lengths;
|
||||||
|
|
||||||
|
/** Relative offsets of the fields. */
|
||||||
|
protected int[] offsets;
|
||||||
|
|
||||||
|
/** Column names of the fields. */
|
||||||
|
protected String[] names;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a schema for the given number of fields.
|
||||||
|
*/
|
||||||
|
public Schema(int fldcnt) {
|
||||||
|
types = new int[fldcnt];
|
||||||
|
lengths = new int[fldcnt];
|
||||||
|
offsets = new int[fldcnt];
|
||||||
|
names = new String[fldcnt];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the type, length, and name for the given field; this automatically
|
||||||
|
* calculates its offset, provided that fields are set in ascending order.
|
||||||
|
*/
|
||||||
|
public void initField(int fldno, int type, int length, String name) {
|
||||||
|
|
||||||
|
// save the type, length, and name
|
||||||
|
types[fldno] = type;
|
||||||
|
lengths[fldno] = length;
|
||||||
|
names[fldno] = name;
|
||||||
|
|
||||||
|
// calculate the relative offset
|
||||||
|
if (fldno > 0) {
|
||||||
|
offsets[fldno] = offsets[fldno - 1] + lengths[fldno - 1];
|
||||||
|
} else {
|
||||||
|
offsets[fldno] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public void initField(int fldno, int type, int length, String name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies a field from another schema.
|
||||||
|
*
|
||||||
|
* @param fldno number of this field to set
|
||||||
|
* @param schema contains the info to copy
|
||||||
|
* @param srcno number of the field to copy
|
||||||
|
*/
|
||||||
|
public void initField(int fldno, Schema schema, int srcno) {
|
||||||
|
initField(fldno, schema.types[srcno], schema.lengths[srcno],
|
||||||
|
schema.names[srcno]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and returns a new schema resulting from joining two schemas.
|
||||||
|
*
|
||||||
|
* @param s1 the left schema
|
||||||
|
* @param s2 the right schema
|
||||||
|
*/
|
||||||
|
public static Schema join(Schema s1, Schema s2) {
|
||||||
|
|
||||||
|
// construct the new schema
|
||||||
|
int s1cnt = s1.getCount();
|
||||||
|
int s2cnt = s2.getCount();
|
||||||
|
Schema schema = new Schema(s1cnt + s2cnt);
|
||||||
|
|
||||||
|
// copy all fields from s1 and s2
|
||||||
|
int fldno = 0;
|
||||||
|
for (int i = 0; i < s1cnt; i++) {
|
||||||
|
schema.initField(fldno++, s1, i);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s2cnt; i++) {
|
||||||
|
schema.initField(fldno++, s2, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the resulting schema
|
||||||
|
return schema;
|
||||||
|
|
||||||
|
} // public static Schema join(Schema s1, Schema s2)
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of fields in the schema.
|
||||||
|
*/
|
||||||
|
public int getCount() {
|
||||||
|
// all arrays are same length
|
||||||
|
return types.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the size of a tuple (in bytes).
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
int len = 0;
|
||||||
|
for (int i = 0; i < lengths.length; i++) {
|
||||||
|
len += lengths[i];
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of the given field.
|
||||||
|
*/
|
||||||
|
public int fieldType(int fldno) {
|
||||||
|
return types[fldno];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the length of the given field.
|
||||||
|
*/
|
||||||
|
public int fieldLength(int fldno) {
|
||||||
|
return lengths[fldno];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the offset of the given field.
|
||||||
|
*/
|
||||||
|
public int fieldOffset(int fldno) {
|
||||||
|
return offsets[fldno];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the given field.
|
||||||
|
*/
|
||||||
|
public String fieldName(int fldno) {
|
||||||
|
return names[fldno];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of the field with the given name, or -1 if not found.
|
||||||
|
*/
|
||||||
|
public int fieldNumber(String name) {
|
||||||
|
for (int i = 0; i < names.length; i++) {
|
||||||
|
if (names[i].equalsIgnoreCase(name)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the schema (i.e. the first lines of typical query output).
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
|
||||||
|
// print and space the column names
|
||||||
|
int len = 0;
|
||||||
|
for (int i = 0; i < types.length; i++) {
|
||||||
|
|
||||||
|
// print the column name
|
||||||
|
System.out.print(names[i]);
|
||||||
|
|
||||||
|
// figure out the padding
|
||||||
|
int collen = Math.max(names[i].length(), MIN_WIDTH);
|
||||||
|
if (types[i] == AttrType.STRING) {
|
||||||
|
collen = Math.max(lengths[i], collen);
|
||||||
|
}
|
||||||
|
len += collen;
|
||||||
|
|
||||||
|
// pad the output to the field length
|
||||||
|
for (int j = 0; j < collen - names[i].length(); j++) {
|
||||||
|
System.out.print(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
} // for
|
||||||
|
|
||||||
|
// print the line separator
|
||||||
|
System.out.println();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
System.out.print('-');
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
} // public void print()
|
||||||
|
|
||||||
|
} // public class Schema
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The selection operator specifies which tuples to retain under a condition; in
|
||||||
|
* Minibase, this condition is simply a set of independent predicates logically
|
||||||
|
* connected by OR operators.
|
||||||
|
*/
|
||||||
|
public class Selection extends Iterator {
|
||||||
|
|
||||||
|
private Iterator iter = null;
|
||||||
|
private Predicate[] preds = null;
|
||||||
|
//boolean hasNext;
|
||||||
|
Tuple next = null;
|
||||||
|
private boolean consumed;
|
||||||
|
private boolean isOpen;
|
||||||
|
/**
|
||||||
|
* Constructs a selection, given the underlying iterator and predicates.
|
||||||
|
*/
|
||||||
|
public Selection(Iterator aIter, Predicate... aPreds) {
|
||||||
|
this.schema = aIter.getSchema();
|
||||||
|
this.iter = aIter;
|
||||||
|
this.preds = aPreds;
|
||||||
|
this.consumed = true;
|
||||||
|
this.isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
//Your code here
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public class Selection extends Iterator
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The simplest of all join algorithms: nested loops (see textbook, 3rd edition,
|
||||||
|
* section 14.4.1, page 454).
|
||||||
|
*/
|
||||||
|
public class SimpleJoin extends Iterator {
|
||||||
|
|
||||||
|
private Iterator outer;
|
||||||
|
private Iterator inner;
|
||||||
|
private Predicate[] preds;
|
||||||
|
|
||||||
|
private boolean startJoin = true;
|
||||||
|
Tuple leftTuple;
|
||||||
|
|
||||||
|
// boolean variable to indicate whether the pre-fetched tuple is consumed or not
|
||||||
|
private boolean nextTupleIsConsumed;
|
||||||
|
|
||||||
|
// pre-fetched tuple
|
||||||
|
private Tuple nextTuple;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a join, given the left and right iterators and join predicates
|
||||||
|
* (relative to the combined schema).
|
||||||
|
*/
|
||||||
|
public SimpleJoin(Iterator left, Iterator right, Predicate... preds) {
|
||||||
|
|
||||||
|
this.outer = left;
|
||||||
|
this.inner = right;
|
||||||
|
this.preds = preds;
|
||||||
|
this.schema = Schema.join(left.schema, right.schema);
|
||||||
|
|
||||||
|
nextTupleIsConsumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives a one-line explanation of the iterator, repeats the call on any
|
||||||
|
* child iterators, and increases the indent depth along the way.
|
||||||
|
*/
|
||||||
|
public void explain(int depth) {
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the iterator, i.e. as if it were just constructed.
|
||||||
|
*/
|
||||||
|
public void restart() {
|
||||||
|
|
||||||
|
outer.restart();
|
||||||
|
inner.restart();
|
||||||
|
nextTupleIsConsumed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the iterator is open; false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isOpen() {
|
||||||
|
|
||||||
|
return outer.isOpen() && inner.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the iterator, releasing any resources (i.e. pinned pages).
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
outer.close();
|
||||||
|
inner.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are more tuples, false otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public boolean hasNext() {
|
||||||
|
|
||||||
|
if (!nextTupleIsConsumed)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!outer.hasNext())
|
||||||
|
//if(!inner.hasNext() && !outer.hasNext()) // Piazza post 116
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Tuple rightTuple;
|
||||||
|
|
||||||
|
if (startJoin) {
|
||||||
|
leftTuple = outer.getNext();
|
||||||
|
startJoin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
while (inner.hasNext()) {
|
||||||
|
|
||||||
|
rightTuple = inner.getNext();
|
||||||
|
|
||||||
|
// try to match
|
||||||
|
nextTuple = Tuple.join(leftTuple, rightTuple, this.schema);
|
||||||
|
for (int i = 0; i < preds.length; i++)
|
||||||
|
if (preds[i].evaluate(nextTuple)) {
|
||||||
|
nextTupleIsConsumed = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outer.hasNext()) {
|
||||||
|
leftTuple = outer.getNext();
|
||||||
|
inner.restart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next tuple in the iteration.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if no more tuples
|
||||||
|
*/
|
||||||
|
public Tuple getNext() {
|
||||||
|
|
||||||
|
nextTupleIsConsumed = true;
|
||||||
|
return nextTuple;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
package relop;
|
||||||
|
|
||||||
|
import global.AttrType;
|
||||||
|
import global.Convert;
|
||||||
|
import global.RID;
|
||||||
|
import heap.HeapFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each tuple in a relation is a collection of bytes that must fit within a
|
||||||
|
* single page. The Tuple class provides a logical view of fields, allowing get
|
||||||
|
* and set operations while automatically handling offsets and type conversions.
|
||||||
|
*/
|
||||||
|
public class Tuple {
|
||||||
|
|
||||||
|
/** Page buffer containing this tuple. */
|
||||||
|
protected byte[] data;
|
||||||
|
|
||||||
|
/** Schema information for the fields. */
|
||||||
|
protected Schema schema;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new empty tuple, given its schema.
|
||||||
|
*
|
||||||
|
* @param schema logical information for the fields
|
||||||
|
*/
|
||||||
|
public Tuple(Schema schema) {
|
||||||
|
this.schema = schema;
|
||||||
|
data = new byte[schema.getLength()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tuple, given its schema and values.
|
||||||
|
*
|
||||||
|
* @param schema logical information for the fields
|
||||||
|
* @param values individual data values of the fields
|
||||||
|
*/
|
||||||
|
public Tuple(Schema schema, Object... values) {
|
||||||
|
this.schema = schema;
|
||||||
|
data = new byte[schema.getLength()];
|
||||||
|
setAllFields(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new tuple, given existing values to wrap.
|
||||||
|
*
|
||||||
|
* @param schema logical information for the fields
|
||||||
|
* @param data record buffer containing the tuple
|
||||||
|
*/
|
||||||
|
public Tuple(Schema schema, byte[] data) {
|
||||||
|
this.schema = schema;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds and returns a new tuple resulting from joining two tuples.
|
||||||
|
*
|
||||||
|
* @param t1 the left tuple
|
||||||
|
* @param t2 the right tuple
|
||||||
|
* @param schema of the result
|
||||||
|
*/
|
||||||
|
public static Tuple join(Tuple t1, Tuple t2, Schema schema) {
|
||||||
|
|
||||||
|
// construct the new tuple
|
||||||
|
int t1cnt = t1.schema.getCount();
|
||||||
|
int t2cnt = t2.schema.getCount();
|
||||||
|
Tuple tuple = new Tuple(schema);
|
||||||
|
|
||||||
|
// copy all fields from t1 and t2
|
||||||
|
int fldno = 0;
|
||||||
|
for (int i = 0; i < t1cnt; i++) {
|
||||||
|
tuple.setField(fldno++, t1.getField(i));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < t2cnt; i++) {
|
||||||
|
tuple.setField(fldno++, t2.getField(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the resulting tuple
|
||||||
|
return tuple;
|
||||||
|
|
||||||
|
} // public static Tuple join(Tuple t1, Tuple t2, Schema schema)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the underlying data buffer.
|
||||||
|
*/
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the tuple into the given heap file.
|
||||||
|
*/
|
||||||
|
public RID insertIntoFile(HeapFile file) {
|
||||||
|
return file.insertRecord(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a field's value generically.
|
||||||
|
*/
|
||||||
|
public Object getField(int fldno) {
|
||||||
|
|
||||||
|
// refer to the schema for the appropriate conversion
|
||||||
|
switch (schema.fieldType(fldno)) {
|
||||||
|
|
||||||
|
case AttrType.INTEGER:
|
||||||
|
return getIntFld(fldno);
|
||||||
|
|
||||||
|
case AttrType.FLOAT:
|
||||||
|
return getFloatFld(fldno);
|
||||||
|
|
||||||
|
case AttrType.STRING:
|
||||||
|
return getStringFld(fldno);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("invalid attribute type");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public Object getField(int fldno)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a field's value generically, by column name.
|
||||||
|
*/
|
||||||
|
public Object getField(String fldName) {
|
||||||
|
return getField(schema.fieldNumber(fldName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a field's value generically.
|
||||||
|
*/
|
||||||
|
public void setField(int fldno, Object val) {
|
||||||
|
|
||||||
|
// refer to the schema for the appropriate conversion
|
||||||
|
switch (schema.fieldType(fldno)) {
|
||||||
|
|
||||||
|
case AttrType.INTEGER:
|
||||||
|
setIntFld(fldno, (Integer) val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.FLOAT:
|
||||||
|
setFloatFld(fldno, (Float) val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.STRING:
|
||||||
|
setStringFld(fldno, (String) val);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("invalid attribute type");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public void setField(int fldno, Object val)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a field's value generically, by column name.
|
||||||
|
*/
|
||||||
|
public void setField(String fldName, Object val) {
|
||||||
|
setField(schema.fieldNumber(fldName), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns generic values for all fields.
|
||||||
|
*/
|
||||||
|
public Object[] getAllFields() {
|
||||||
|
|
||||||
|
Object[] values = new Object[schema.getCount()];
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
values[i] = getField(i);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
|
||||||
|
} // public Object[] getAllFields()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all fields, given generic values.
|
||||||
|
*/
|
||||||
|
public void setAllFields(Object... values) {
|
||||||
|
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
setField(i, values[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public void setAllFields(Object... values)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an integer field.
|
||||||
|
*/
|
||||||
|
public int getIntFld(int fldno) {
|
||||||
|
return Convert.getIntValue(schema.fieldOffset(fldno), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an integer field.
|
||||||
|
*/
|
||||||
|
public void setIntFld(int fldno, int val) {
|
||||||
|
Convert.setIntValue(val, schema.fieldOffset(fldno), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a float field.
|
||||||
|
*/
|
||||||
|
public float getFloatFld(int fldno) {
|
||||||
|
return Convert.getFloatValue(schema.fieldOffset(fldno), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a float field.
|
||||||
|
*/
|
||||||
|
public void setFloatFld(int fldno, float val) {
|
||||||
|
Convert.setFloatValue(val, schema.fieldOffset(fldno), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a string field.
|
||||||
|
*/
|
||||||
|
public String getStringFld(int fldno) {
|
||||||
|
return Convert.getStringValue(schema.fieldOffset(fldno), data, schema
|
||||||
|
.fieldLength(fldno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a string field (automatically truncates, if necessary).
|
||||||
|
*/
|
||||||
|
public void setStringFld(int fldno, String val) {
|
||||||
|
|
||||||
|
// truncate the string if too long
|
||||||
|
int len = schema.fieldLength(fldno);
|
||||||
|
if (val.length() > len) {
|
||||||
|
val = val.substring(0, len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the string and zero out the rest
|
||||||
|
int off = schema.fieldOffset(fldno);
|
||||||
|
Convert.setStringValue(val, off, data);
|
||||||
|
for (int i = val.length(); i < len; i++) {
|
||||||
|
data[off + i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // public void setStringFld(int fldno, String val)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the tuple in a human-readable format.
|
||||||
|
*/
|
||||||
|
public void print() {
|
||||||
|
|
||||||
|
// print everything as a string, with padding
|
||||||
|
String str = null;
|
||||||
|
int cnt = schema.getCount();
|
||||||
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
switch (schema.fieldType(i)) {
|
||||||
|
|
||||||
|
case AttrType.INTEGER:
|
||||||
|
str = Integer.toString(getIntFld(i));
|
||||||
|
System.out.print(str);
|
||||||
|
padOutput(0, str.length());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.FLOAT:
|
||||||
|
str = Float.toString(getFloatFld(i));
|
||||||
|
System.out.print(str);
|
||||||
|
padOutput(0, str.length());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AttrType.STRING:
|
||||||
|
str = getStringFld(i);
|
||||||
|
System.out.print(str);
|
||||||
|
padOutput(schema.fieldLength(i), str.length());
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
} // public void print()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pads the current output to make columns line up.
|
||||||
|
*
|
||||||
|
* @param fieldLen minimum length of the current field
|
||||||
|
* @param outputLen length of the current output so far
|
||||||
|
*/
|
||||||
|
protected void padOutput(int fieldLen, int outputLen) {
|
||||||
|
|
||||||
|
fieldLen = Math.max(Schema.MIN_WIDTH, fieldLen);
|
||||||
|
for (int j = 0; j < fieldLen - outputLen; j++) {
|
||||||
|
System.out.print(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
} // protected void padOutput(int fieldLen, int outputLen)
|
||||||
|
|
||||||
|
|
||||||
|
// *** DO NOT CHANGE ANY EXISTING METHODS OR VARIABLES BELOW THIS LINE ***
|
||||||
|
//String format: field0|field1|field2|...fieldN|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
String res = "";
|
||||||
|
for(int i = 0; i < schema.getCount(); i++)
|
||||||
|
{
|
||||||
|
switch(schema.fieldType(i))
|
||||||
|
{
|
||||||
|
case AttrType.FLOAT:
|
||||||
|
res += Float.toString(getFloatFld(i)) + "";
|
||||||
|
break;
|
||||||
|
case AttrType.INTEGER:
|
||||||
|
res += Integer.toString(getIntFld(i)) + ":";
|
||||||
|
break;
|
||||||
|
case AttrType.STRING:
|
||||||
|
res += getStringFld(i) + ":";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(res.length() > 0) res = res.substring(0, res.length()-1);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // public class Tuple
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
import global.AttrType;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
|
||||||
|
public class Field {
|
||||||
|
|
||||||
|
private int no;
|
||||||
|
private int type;
|
||||||
|
private int length;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
Field(JSONObject obj) {
|
||||||
|
no = ((Long) obj.get("no")).intValue();
|
||||||
|
type = AttrType.fromString((String) obj.get("type"));
|
||||||
|
length = ((Long) obj.get("length")).intValue();
|
||||||
|
name = (String) obj.get("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getValue(String val) {
|
||||||
|
switch (type) {
|
||||||
|
case AttrType.INTEGER: {
|
||||||
|
return Integer.parseInt(val.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
case AttrType.FLOAT: {
|
||||||
|
return Float.parseFloat(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNo() {
|
||||||
|
return no;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
import global.AttrOperator;
|
||||||
|
import global.AttrType;
|
||||||
|
import heap.HeapFile;
|
||||||
|
import relop.FileScan;
|
||||||
|
import relop.HashJoin;
|
||||||
|
import relop.Predicate;
|
||||||
|
import relop.Projection;
|
||||||
|
import relop.Schema;
|
||||||
|
import relop.Selection;
|
||||||
|
import relop.SimpleJoin;
|
||||||
|
import relop.Tuple;
|
||||||
|
|
||||||
|
// YOUR CODE FOR PART3 SHOULD GO HERE.
|
||||||
|
|
||||||
|
public class QEPTest extends TestDriver
|
||||||
|
{
|
||||||
|
/** The display name of the test suite. */
|
||||||
|
private static final String TEST_NAME = "QEP Tests";
|
||||||
|
|
||||||
|
|
||||||
|
/** Employee table schema. */
|
||||||
|
private static Schema s_emp;
|
||||||
|
|
||||||
|
/** Department table schema. */
|
||||||
|
private static Schema s_dept;
|
||||||
|
|
||||||
|
private static String empFilePath;
|
||||||
|
private static String deptFilePath;
|
||||||
|
|
||||||
|
private static HeapFile empHeapFile;
|
||||||
|
private static HeapFile deptHeapFile;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test application entry point; runs all tests.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void main(String argv[]) throws IOException
|
||||||
|
{
|
||||||
|
empFilePath = argv[0] + "/Employee.txt";
|
||||||
|
deptFilePath = argv[0] + "/Department.txt";
|
||||||
|
// create a clean Minibase instance
|
||||||
|
QEPTest qepTest = new QEPTest();
|
||||||
|
qepTest.create_minibase();
|
||||||
|
|
||||||
|
initSchema();
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
|
||||||
|
qepTest.q1();
|
||||||
|
qepTest.q2();
|
||||||
|
qepTest.q3();
|
||||||
|
qepTest.q4();
|
||||||
|
qepTest.q5();
|
||||||
|
qepTest.q6();
|
||||||
|
qepTest.q7();
|
||||||
|
qepTest.q8();
|
||||||
|
|
||||||
|
/*
|
||||||
|
qepTest.q5();
|
||||||
|
qepTest.q5_hash();
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q1()
|
||||||
|
{
|
||||||
|
System.out.println("\n 1) Display for each employee his Name and Salary:\n");
|
||||||
|
FileScan scan = new FileScan(s_emp, empHeapFile);
|
||||||
|
Projection pro = new Projection(scan, s_emp.fieldNumber(Constants.EmpNameColName) , s_emp.fieldNumber(Constants.EmpSalaryColName));
|
||||||
|
pro.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q2()
|
||||||
|
{
|
||||||
|
System.out.println("\n 2) Display the Name for the departments with MinSalary = 1000:\n");
|
||||||
|
FileScan scan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate pred = new Predicate(AttrOperator.EQ, AttrType.COLNAME, Constants.DeptMinSalaryColName, AttrType.FLOAT, 1000F);
|
||||||
|
Selection sel = new Selection(scan, pred);
|
||||||
|
Projection pro = new Projection(sel, s_dept.fieldNumber(Constants.DeptNameColName));
|
||||||
|
pro.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q3()
|
||||||
|
{
|
||||||
|
System.out.println("\n 3) Display the Name for the departments with MinSalary = MaxSalary:\n");
|
||||||
|
FileScan scan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate pred = new Predicate(AttrOperator.EQ, AttrType.COLNAME, Constants.DeptMinSalaryColName, AttrType.COLNAME, Constants.DeptMaxSalaryColName);
|
||||||
|
Selection sel = new Selection(scan, pred);
|
||||||
|
Projection pro = new Projection(sel, s_dept.fieldNumber(Constants.DeptNameColName));
|
||||||
|
pro.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q4()
|
||||||
|
{
|
||||||
|
System.out.println("\n 4) Display the Name for employees whose Age > 30 and Salary < 1000:\n");
|
||||||
|
FileScan scan = new FileScan(s_emp, empHeapFile);
|
||||||
|
Predicate predAge = new Predicate(AttrOperator.GT, AttrType.COLNAME, Constants.EmpAgeColName, AttrType.FLOAT, 30F);
|
||||||
|
Predicate predSalary = new Predicate(AttrOperator.LT, AttrType.COLNAME, Constants.EmpSalaryColName, AttrType.FLOAT, 1000F);
|
||||||
|
Selection selAge = new Selection(scan, predAge);
|
||||||
|
Selection selSalary = new Selection(selAge, predSalary);
|
||||||
|
Projection projName = new Projection(selSalary, s_emp.fieldNumber(Constants.EmpNameColName));
|
||||||
|
projName.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q5()
|
||||||
|
{
|
||||||
|
System.out.println("\n 5) For each employee, display his Salary and the Name of his department:\n");
|
||||||
|
FileScan empScan = new FileScan(s_emp, empHeapFile);
|
||||||
|
FileScan deptScan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate joinPred = new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 8);
|
||||||
|
SimpleJoin join = new SimpleJoin(deptScan, empScan, joinPred);
|
||||||
|
Projection proj = new Projection(join, 7, 1);
|
||||||
|
proj.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q6()
|
||||||
|
{
|
||||||
|
System.out.println("\n 6) Display the Name and Salary for employees who work in the department that has DeptId = 3:\n");
|
||||||
|
FileScan scan = new FileScan(s_emp, empHeapFile);
|
||||||
|
Predicate predDept = new Predicate(AttrOperator.EQ, AttrType.COLNAME, Constants.EmpDeptIDColName, AttrType.INTEGER, 3);
|
||||||
|
Selection sel = new Selection(scan, predDept);
|
||||||
|
Projection proj = new Projection(sel, s_emp.fieldNumber(Constants.EmpNameColName), s_emp.fieldNumber(Constants.EmpSalaryColName));
|
||||||
|
proj.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q7()
|
||||||
|
{
|
||||||
|
System.out.println("\n 7) Display the Salary for each employee who works in a department that has MaxSalary > 100000:\n");
|
||||||
|
FileScan empScan = new FileScan(s_emp, empHeapFile);
|
||||||
|
FileScan deptScan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate predMaxSalary = new Predicate(AttrOperator.GT, AttrType.COLNAME, Constants.DeptMaxSalaryColName, AttrType.FLOAT, 100000F);
|
||||||
|
Selection selDept = new Selection(deptScan, predMaxSalary);
|
||||||
|
Predicate joinPred = new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 8);
|
||||||
|
SimpleJoin join = new SimpleJoin(selDept, empScan, joinPred);
|
||||||
|
Projection proj = new Projection(join, 7);
|
||||||
|
proj.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void q8()
|
||||||
|
{
|
||||||
|
System.out.println("\n 8) Display the Name for each employee whose Salary is less than the MinSalary of his department:\n");
|
||||||
|
FileScan empScan = new FileScan(s_emp, empHeapFile);
|
||||||
|
FileScan deptScan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate joinPred = new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 8);
|
||||||
|
SimpleJoin join = new SimpleJoin(deptScan, empScan, joinPred);
|
||||||
|
Predicate pred = new Predicate(AttrOperator.LT, AttrType.FIELDNO, 7, AttrType.FIELDNO, 2);
|
||||||
|
Selection sel = new Selection(join, pred);
|
||||||
|
Projection proj = new Projection(sel, 5);
|
||||||
|
proj.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void q5_hash()
|
||||||
|
{
|
||||||
|
System.out.println("\n 5) For each employee, display his Salary and the Name of his department:\n");
|
||||||
|
FileScan empScan = new FileScan(s_emp, empHeapFile);
|
||||||
|
FileScan deptScan = new FileScan(s_dept, deptHeapFile);
|
||||||
|
Predicate joinPred = new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 8);
|
||||||
|
//SimpleJoin join = new SimpleJoin(deptScan, empScan, joinPred);
|
||||||
|
HashJoin join = new HashJoin(deptScan, empScan, 0, 4);
|
||||||
|
Projection proj = new Projection(join, 7, 1);
|
||||||
|
proj.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void initSchema()
|
||||||
|
{
|
||||||
|
//initialize schema for the "Employee" table
|
||||||
|
s_emp = new Schema(5);
|
||||||
|
s_emp.initField(0, AttrType.INTEGER, 4, Constants.EmpIdColName);
|
||||||
|
s_emp.initField(1, AttrType.STRING, 40, Constants.EmpNameColName);
|
||||||
|
s_emp.initField(2, AttrType.FLOAT, 4, Constants.EmpAgeColName);
|
||||||
|
s_emp.initField(3, AttrType.FLOAT, 4, Constants.EmpSalaryColName);
|
||||||
|
s_emp.initField(4, AttrType.INTEGER, 4, Constants.EmpDeptIDColName);
|
||||||
|
|
||||||
|
//initialize schema for the "Department" table
|
||||||
|
s_dept = new Schema(4);
|
||||||
|
s_dept.initField(0, AttrType.INTEGER, 4, Constants.DeptIdColName);
|
||||||
|
s_dept.initField(1, AttrType.STRING, 40, Constants.DeptNameColName);
|
||||||
|
s_dept.initField(2, AttrType.FLOAT, 4, Constants.DeptMinSalaryColName);
|
||||||
|
s_dept.initField(3, AttrType.FLOAT, 4, Constants.DeptMaxSalaryColName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadData() throws IOException
|
||||||
|
{
|
||||||
|
empHeapFile = new HeapFile(null);
|
||||||
|
loadEmpTable(empHeapFile);
|
||||||
|
|
||||||
|
deptHeapFile = new HeapFile(null);
|
||||||
|
loadDeptTable(deptHeapFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected static void loadEmpTable(HeapFile file) throws IOException
|
||||||
|
{
|
||||||
|
Tuple tuple = new Tuple(s_emp);
|
||||||
|
BufferedReader reader = new BufferedReader(new FileReader(empFilePath));
|
||||||
|
reader.readLine(); //ignore the header
|
||||||
|
String line = "";
|
||||||
|
String[] temp = null;
|
||||||
|
int empId; String empName; float age; float salary; int deptId;
|
||||||
|
while((line = reader.readLine()) != null)
|
||||||
|
{
|
||||||
|
temp = line.split(",");
|
||||||
|
empId = Integer.parseInt(temp[0].trim());
|
||||||
|
empName = temp[1].trim();
|
||||||
|
age = Float.parseFloat(temp[2].trim());
|
||||||
|
salary = Float.parseFloat(temp[3].trim());
|
||||||
|
deptId = Integer.parseInt(temp[4].trim());
|
||||||
|
tuple.setAllFields(empId, empName, age, salary, deptId);
|
||||||
|
tuple.insertIntoFile(file);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void loadDeptTable(HeapFile file) throws IOException
|
||||||
|
{
|
||||||
|
Tuple tuple = new Tuple(s_dept);
|
||||||
|
BufferedReader reader = new BufferedReader(new FileReader(deptFilePath));
|
||||||
|
reader.readLine(); //ignore the header
|
||||||
|
String line = "";
|
||||||
|
String[] temp = null;
|
||||||
|
int deptId; String deptName; float minSalary; float maxSalary;
|
||||||
|
while((line = reader.readLine()) != null)
|
||||||
|
{
|
||||||
|
temp = line.split(",");
|
||||||
|
deptId = Integer.parseInt(temp[0].trim());
|
||||||
|
deptName = temp[1].trim();
|
||||||
|
minSalary = Float.parseFloat(temp[2].trim());
|
||||||
|
maxSalary = Float.parseFloat(temp[3].trim());
|
||||||
|
tuple.setAllFields(deptId, deptName, minSalary, maxSalary);
|
||||||
|
tuple.insertIntoFile(file);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Constants
|
||||||
|
{
|
||||||
|
|
||||||
|
public final static String EmpIdColName = "EmpId";
|
||||||
|
public final static String EmpNameColName = "Name";
|
||||||
|
public final static String EmpAgeColName = "Age";
|
||||||
|
public final static String EmpSalaryColName = "Salary";
|
||||||
|
public final static String EmpDeptIDColName = "DeptID";
|
||||||
|
|
||||||
|
public final static String DeptIdColName = "DeptId";
|
||||||
|
public final static String DeptNameColName = "Name";
|
||||||
|
public final static String DeptMinSalaryColName = "MinSalary";
|
||||||
|
public final static String DeptMaxSalaryColName = "MaxSalary";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
import org.json.simple.JSONArray;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.json.simple.parser.ParseException;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.junit.runner.JUnitCore;
|
||||||
|
import org.junit.runner.Result;
|
||||||
|
import org.junit.runner.notification.Failure;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import global.AttrOperator;
|
||||||
|
import global.AttrType;
|
||||||
|
import global.RID;
|
||||||
|
import global.SearchKey;
|
||||||
|
import heap.HeapFile;
|
||||||
|
import index.HashIndex;
|
||||||
|
import relop.FileScan;
|
||||||
|
import relop.HashJoin;
|
||||||
|
import relop.IndexScan;
|
||||||
|
import relop.KeyScan;
|
||||||
|
import relop.Predicate;
|
||||||
|
import relop.Projection;
|
||||||
|
import relop.Schema;
|
||||||
|
import relop.Selection;
|
||||||
|
import relop.SimpleJoin;
|
||||||
|
import relop.Tuple;
|
||||||
|
import relop.Iterator;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import com.opencsv.CSVReader;
|
||||||
|
import org.json.simple.parser.JSONParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for the relop layer.
|
||||||
|
*/
|
||||||
|
public class ROTest extends TestDriver {
|
||||||
|
|
||||||
|
private static ROTest rot;
|
||||||
|
|
||||||
|
/** The display name of the test suite. */
|
||||||
|
private static final String TEST_NAME = "relational operator tests";
|
||||||
|
|
||||||
|
/** Size of tables in test3. */
|
||||||
|
private static final int SUPER_SIZE = 2000;
|
||||||
|
|
||||||
|
private Scanner in;
|
||||||
|
|
||||||
|
/** Drivers schema/table/index */
|
||||||
|
private static Schema s_drivers;
|
||||||
|
private static HeapFile f_drivers;
|
||||||
|
private static HashIndex idx_drivers;
|
||||||
|
|
||||||
|
private static Schema s_driversBig;
|
||||||
|
private static HeapFile f_driversBig;
|
||||||
|
private static HashIndex idx_driversBig;
|
||||||
|
/** Rides schema/table/index */
|
||||||
|
|
||||||
|
private static Schema s_rides;
|
||||||
|
private static HeapFile f_rides;
|
||||||
|
private static HashIndex idx_rides;
|
||||||
|
|
||||||
|
private static Schema s_ridesBig;
|
||||||
|
private static HeapFile f_ridesBig;
|
||||||
|
private static HashIndex idx_ridesBig;
|
||||||
|
|
||||||
|
/** Groups schema/table/index */
|
||||||
|
private static Schema s_groups;
|
||||||
|
private static HeapFile f_groups;
|
||||||
|
private static HashIndex idx_groups;
|
||||||
|
|
||||||
|
private static Schema s_groupsBig;
|
||||||
|
private static HeapFile f_groupsBig;
|
||||||
|
private static HashIndex idx_groupsBig;
|
||||||
|
|
||||||
|
/** Expected result strings for test cases */
|
||||||
|
private static HashMap<String, String> results;
|
||||||
|
|
||||||
|
/** */
|
||||||
|
private static void loadTable(Schema schema, HeapFile f_table, HashIndex idx_table, String table)
|
||||||
|
throws FileNotFoundException, IOException, ParseException {
|
||||||
|
|
||||||
|
// initialize schema for table
|
||||||
|
InputStream in = ClassLoader.getSystemResourceAsStream(table.concat(".json"));
|
||||||
|
JSONObject jsonObj = (JSONObject) new JSONParser().parse(new InputStreamReader(in));
|
||||||
|
JSONArray jsonFields = (JSONArray) jsonObj.get("schema");
|
||||||
|
Field[] fields = new Field[jsonFields.size()];
|
||||||
|
|
||||||
|
int key = ((Long) jsonObj.get("key")).intValue();
|
||||||
|
int xx = 0;
|
||||||
|
for (Object obj : jsonFields) {
|
||||||
|
Field field = new Field((JSONObject) obj);
|
||||||
|
fields[xx] = field; xx++;
|
||||||
|
schema.initField(field.getNo(), field.getType(), field.getLength(), field.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// populate relation
|
||||||
|
in = ClassLoader.getSystemResourceAsStream(table.concat(".csv"));
|
||||||
|
try (CSVReader csvReader = new CSVReader(new InputStreamReader(in))) {
|
||||||
|
String[] values = null;
|
||||||
|
Tuple tuple = new Tuple(schema);
|
||||||
|
while((values = csvReader.readNext()) != null) {
|
||||||
|
|
||||||
|
for (int i=0; i<values.length; i++) {
|
||||||
|
tuple.setField(i, fields[i].getValue(values[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
idx_table.insertEntry(new SearchKey(fields[key].getValue(values[key])), f_table.insertRecord(tuple.getData()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void execute_and_compare(String testDesc, String id, Iterator it)
|
||||||
|
{
|
||||||
|
it.execute();
|
||||||
|
it.close();
|
||||||
|
String[] sol = results.get(id).split("|");
|
||||||
|
Arrays.sort(sol);
|
||||||
|
String[] res = it.getResult().split("|");
|
||||||
|
Arrays.sort(res);
|
||||||
|
assertTrue("FAILURE: " + testDesc + " output {" + it.getResult() + "} did not match expected result, should be " + results.get(id), Arrays.equals(sol, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Result result = JUnitCore.runClasses(tests.ROTest.class);
|
||||||
|
|
||||||
|
for (Failure failure : result.getFailures()) {
|
||||||
|
System.out.println(failure.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(result.wasSuccessful());
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupDB() throws Exception {
|
||||||
|
// create a clean Minibase instance
|
||||||
|
rot = new ROTest();
|
||||||
|
rot.create_minibase();
|
||||||
|
|
||||||
|
results = new HashMap<String, String>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Scanner in = new Scanner(new File("solution.txt"));
|
||||||
|
String line;
|
||||||
|
String[] res;
|
||||||
|
while(in.hasNextLine())
|
||||||
|
{
|
||||||
|
line = in.nextLine();
|
||||||
|
res = line.split("=");
|
||||||
|
results.put(res[0],res[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Drivers" table
|
||||||
|
s_drivers = new Schema(5);
|
||||||
|
f_drivers = new HeapFile("drivers");
|
||||||
|
idx_drivers = new HashIndex("drivers_idx");
|
||||||
|
loadTable(s_drivers, f_drivers, idx_drivers, "drivers");
|
||||||
|
|
||||||
|
|
||||||
|
s_driversBig = new Schema(5);
|
||||||
|
f_driversBig = new HeapFile("driversBig");
|
||||||
|
idx_driversBig = new HashIndex("drivers_idxBig");
|
||||||
|
loadTable(s_driversBig, f_driversBig, idx_driversBig, "driversBig");
|
||||||
|
// "Rides" table
|
||||||
|
s_rides = new Schema(4);
|
||||||
|
f_rides = new HeapFile("rides");
|
||||||
|
idx_rides = new HashIndex("rides_idx");
|
||||||
|
loadTable(s_rides, f_rides, idx_rides, "rides");
|
||||||
|
|
||||||
|
s_ridesBig = new Schema(4);
|
||||||
|
f_ridesBig = new HeapFile("ridesBig");
|
||||||
|
idx_ridesBig = new HashIndex("rides_idxBig");
|
||||||
|
loadTable(s_ridesBig, f_ridesBig, idx_ridesBig, "ridesBig");
|
||||||
|
|
||||||
|
// "Groups" table
|
||||||
|
s_groupsBig = new Schema(2);
|
||||||
|
f_groupsBig = new HeapFile("groupsBig");
|
||||||
|
idx_groupsBig = new HashIndex("groups_idxBig");
|
||||||
|
loadTable(s_groupsBig, f_groupsBig, idx_groupsBig, "groupsBig");
|
||||||
|
|
||||||
|
s_groups = new Schema(2);
|
||||||
|
f_groups = new HeapFile("groups");
|
||||||
|
idx_groups = new HashIndex("groups_idx");
|
||||||
|
loadTable(s_groups, f_groups, idx_groups, "groups");
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownDB()
|
||||||
|
{
|
||||||
|
idx_drivers.deleteFile();
|
||||||
|
idx_rides.deleteFile();
|
||||||
|
idx_groups.deleteFile();
|
||||||
|
f_rides.deleteFile();
|
||||||
|
f_drivers.deleteFile();
|
||||||
|
f_groups.deleteFile();
|
||||||
|
rot.delete_minibase();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFileScan() {
|
||||||
|
//Scan drivers table
|
||||||
|
Iterator fscan = new FileScan(s_drivers, f_drivers);
|
||||||
|
execute_and_compare("Filescan", "filescan", fscan);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIndexScan() {
|
||||||
|
//Scan drivers index
|
||||||
|
Iterator idxscan = new IndexScan(s_drivers, idx_drivers, f_drivers);
|
||||||
|
execute_and_compare("IndexScan", "idxscan", idxscan);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeyScan() {
|
||||||
|
//Scan drivers index for key 20f
|
||||||
|
Iterator keyscan = new KeyScan(s_drivers, idx_drivers, new SearchKey(20f), f_drivers);
|
||||||
|
execute_and_compare("KeyScan", "keyscan", keyscan);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelection() {
|
||||||
|
//Selection drivers with age > 20
|
||||||
|
Iterator selection = new Selection(new FileScan(s_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.GT, AttrType.COLNAME, "age", AttrType.FLOAT, 20F));
|
||||||
|
execute_and_compare("Selection", "selection", selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectionMultiplePredicates() {
|
||||||
|
Iterator selection_preds = new Selection(new FileScan(s_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.GT, AttrType.COLNAME, "age", AttrType.FLOAT, 23F),
|
||||||
|
new Predicate(AttrOperator.LT, AttrType.COLNAME, "age", AttrType.FLOAT, 19F));
|
||||||
|
execute_and_compare("Selection Multipled Predicates", "selection_preds", selection_preds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjection() {
|
||||||
|
//Projection on Drivers: {FirstName, NumSeats}
|
||||||
|
Iterator projection = new Projection(new FileScan(s_drivers, f_drivers), s_drivers.fieldNumber("FirstName"), s_drivers.fieldNumber("NumSeats"));
|
||||||
|
execute_and_compare("Projection", "projection", projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashJoin() {
|
||||||
|
//HashJoin on Drivers X Rides on DriverID
|
||||||
|
Iterator hashjoin = new HashJoin(new FileScan(s_drivers, f_drivers),
|
||||||
|
new FileScan(s_rides, f_rides), 0, 0);
|
||||||
|
execute_and_compare("Hash Join", "hashjoin", hashjoin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSelectionPipelining() {
|
||||||
|
//Test all possible Iterator inputs to Selection
|
||||||
|
Iterator sel_idx = new Selection(new IndexScan(s_drivers, idx_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining IndexScan", "sel_idx", sel_idx);
|
||||||
|
Iterator sel_key = new Selection(new KeyScan(s_drivers, idx_drivers, new SearchKey(20F), f_drivers),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining Keyscan", "sel_key", sel_key);
|
||||||
|
Iterator sel_sel = new Selection(new Selection(new FileScan(s_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "Age", AttrType.FLOAT, 20F)),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining Selection", "sel_sel", sel_sel);
|
||||||
|
Iterator sel_proj = new Selection(new Projection(
|
||||||
|
new FileScan(s_drivers, f_drivers), s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("FirstName")),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining Projection", "sel_proj", sel_proj);
|
||||||
|
/* Iterator sel_sj = new Selection(new SimpleJoin(new FileScan(s_drivers, f_drivers), new FileScan(s_rides, f_rides),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 5)),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining Simple Join", "sel_sj", sel_sj);
|
||||||
|
*/
|
||||||
|
Iterator sel_hj = new Selection(new HashJoin(new FileScan(s_drivers, f_drivers), new FileScan(s_rides, f_rides),0,0),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "FirstName", AttrType.STRING, "Walid"));
|
||||||
|
execute_and_compare("Selection - Pipelining Hash Join", "sel_jh", sel_hj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectionPipelining() {
|
||||||
|
//Test all possible Iterator inputs to HashJoin
|
||||||
|
Iterator proj_idx = new Projection(new IndexScan(s_drivers, idx_drivers, f_drivers),
|
||||||
|
s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("Age"));
|
||||||
|
execute_and_compare("Projection - Pipelining IndexScan", "proj_idx", proj_idx);
|
||||||
|
Iterator proj_key = new Projection(new KeyScan(s_drivers, idx_drivers, new SearchKey(20F), f_drivers),
|
||||||
|
s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("Age"));
|
||||||
|
execute_and_compare("Projection - Pipelining KeyScan", "proj_key", proj_key);
|
||||||
|
Iterator proj_sel = new Projection(new Selection(new FileScan(s_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "Age", AttrType.FLOAT, 20F)),
|
||||||
|
s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("Age"));
|
||||||
|
execute_and_compare("Projection - Pipelining Selection", "proj_sel", proj_sel);
|
||||||
|
Iterator proj_proj = new Projection(new Projection(new FileScan(s_drivers, f_drivers),
|
||||||
|
s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("FirstName"), s_drivers.fieldNumber("Age")),
|
||||||
|
0, 2);
|
||||||
|
execute_and_compare("Projection - Pipelining Projection", "proj_proj", proj_proj);
|
||||||
|
/*
|
||||||
|
Iterator proj_sj = new Projection(new SimpleJoin(new FileScan(s_drivers, f_drivers), new FileScan(s_rides, f_rides),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.FIELDNO, 0, AttrType.FIELDNO, 5)),
|
||||||
|
0, 3);
|
||||||
|
execute_and_compare("Projection - Pipelining Simple Join", "proj_sj", proj_sj);
|
||||||
|
*/
|
||||||
|
Iterator proj_hj = new Projection(new HashJoin(new FileScan(s_drivers, f_drivers), new FileScan(s_rides, f_rides),0,0),
|
||||||
|
0, 3);
|
||||||
|
execute_and_compare("Projection - Pipelining Hash Join", "proj_hj", proj_hj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashjoinPipelining() {
|
||||||
|
//Test all possible Iterator inputs to HashJoin
|
||||||
|
/*
|
||||||
|
Iterator hj_sel_sj = new HashJoin(new Selection(new FileScan(s_drivers, f_drivers),
|
||||||
|
new Predicate(AttrOperator.GTE, AttrType.COLNAME, "DriverId", AttrType.INTEGER, 1)),
|
||||||
|
new SimpleJoin(new FileScan(s_rides, f_rides), new FileScan(s_groups, f_groups),
|
||||||
|
new Predicate(AttrOperator.EQ, AttrType.COLNAME, "GroupId", AttrType.COLNAME, "DriverId")),0,0);
|
||||||
|
execute_and_compare("Hash Join - Pipelining Selection/Simple Join", "hj_sel_sj", hj_sel_sj);
|
||||||
|
*/
|
||||||
|
Iterator hj_proj_hj = new HashJoin(new Projection(new FileScan(s_drivers, f_drivers), s_drivers.fieldNumber("DriverId"), s_drivers.fieldNumber("Age")),
|
||||||
|
new HashJoin(new FileScan(s_rides, f_rides), new FileScan(s_groups, f_groups), 1, 0), 0, 0);
|
||||||
|
execute_and_compare("Hash Join - Pipelining Projection/Hash Join", "hj_proj_hj", hj_proj_hj);
|
||||||
|
Iterator hj_iscan_kscan = new HashJoin(new IndexScan(s_drivers, idx_drivers, f_drivers),
|
||||||
|
new KeyScan(s_rides, idx_rides, new SearchKey(1), f_rides), 0, 0);
|
||||||
|
execute_and_compare("Hash Join - Pipelining IndexScan/KeyScan", "hj_iscan_kscan", hj_iscan_kscan);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is an EXTRA test to do Drivers X Drivers using HashJoin and we will calibrate this by changing the value of HashTableDup in the HashTableDup class
|
||||||
|
@Test
|
||||||
|
public void testHashJoinOnBigTables() {
|
||||||
|
//HashJoin on Drivers X Rides on DriverID
|
||||||
|
//Anytime the HashTableDup count goes over the threshold, a runtimeexception is thrown
|
||||||
|
Iterator hashSame = new HashJoin(new FileScan(s_drivers, f_drivers),
|
||||||
|
new FileScan(s_drivers, f_drivers), 0, 0);
|
||||||
|
execute_and_compare("Hash Join", "hashSame", hashSame);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is second EXTRA test to do DriversBig X DriversBig using HashJoin and we will calibrate this by changing the value of HashTableDup in the HashTableDup class
|
||||||
|
//Anytime the HashTableDup count goes over the threshold, a runtimeexception is thrown
|
||||||
|
//In addition, any call to Simplejoin throws a RunTimeException
|
||||||
|
@Test
|
||||||
|
public void testHashJoinOnEqualTables() {
|
||||||
|
//HashJoin on Drivers X Rides on DriverID
|
||||||
|
Iterator hashEqual = new HashJoin(new FileScan(s_driversBig, f_driversBig),
|
||||||
|
new FileScan(s_driversBig, f_driversBig), 0, 0);
|
||||||
|
execute_and_compare("Hash Join", "hashEqual", hashEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // class ROTest extends TestDriver
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package tests;
|
||||||
|
|
||||||
|
import global.GlobalConst;
|
||||||
|
import global.Minibase;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h3>Minibase Test Driver</h3>
|
||||||
|
* This base class contains common code to each layer's test suite.
|
||||||
|
*/
|
||||||
|
class TestDriver implements GlobalConst {
|
||||||
|
|
||||||
|
/** Success value, for readability. */
|
||||||
|
protected static final boolean PASS = true;
|
||||||
|
|
||||||
|
/** Failure value, for readability. */
|
||||||
|
protected static final boolean FAIL = false;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Default database file name. */
|
||||||
|
protected String DB_PATH = System.getProperty("user.name") + ".minibase";
|
||||||
|
|
||||||
|
/** Default database size (in pages). */
|
||||||
|
protected int DB_SIZE = 10000;
|
||||||
|
|
||||||
|
/** Default buffer pool size (in pages) */
|
||||||
|
protected int BUF_SIZE = 100;
|
||||||
|
|
||||||
|
/** Default buffer pool replacement policy */
|
||||||
|
protected String BUF_POLICY = "Clock";
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Random generator; use the same seed to make tests deterministic. */
|
||||||
|
protected Random random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter values saved with a particular description.
|
||||||
|
*/
|
||||||
|
protected class CountData {
|
||||||
|
|
||||||
|
public String desc;
|
||||||
|
|
||||||
|
public int reads;
|
||||||
|
|
||||||
|
public int writes;
|
||||||
|
|
||||||
|
public int allocs;
|
||||||
|
|
||||||
|
public int pinned;
|
||||||
|
|
||||||
|
} // protected class CountData
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incremental history of the performance counters; odd elements are snapshots
|
||||||
|
* before the tests, and even elements are after.
|
||||||
|
*/
|
||||||
|
protected ArrayList<CountData> counts;
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the database files from the disk.
|
||||||
|
*/
|
||||||
|
protected void delete_minibase() {
|
||||||
|
new File(DB_PATH).delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new database on the disk.
|
||||||
|
*/
|
||||||
|
protected void create_minibase() {
|
||||||
|
System.out.println("Creating database...\nReplacer: " + BUF_POLICY);
|
||||||
|
new Minibase(DB_PATH, DB_SIZE, BUF_SIZE, BUF_POLICY, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an existing database from the disk.
|
||||||
|
*/
|
||||||
|
protected void load_minibase() {
|
||||||
|
System.out.println("Loading database...\nReplacer: " + BUF_POLICY);
|
||||||
|
new Minibase(DB_PATH, DB_SIZE, BUF_SIZE, BUF_POLICY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the random generator to the default seed.
|
||||||
|
*/
|
||||||
|
protected void initRandom() {
|
||||||
|
random = new Random(74);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the performance counter history.
|
||||||
|
*/
|
||||||
|
protected void initCounts() {
|
||||||
|
counts = new ArrayList<CountData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current performance counters, given the description.
|
||||||
|
*/
|
||||||
|
protected void saveCounts(String desc) {
|
||||||
|
|
||||||
|
// create the new count data
|
||||||
|
CountData data = new CountData();
|
||||||
|
counts.add(data);
|
||||||
|
data.desc = desc;
|
||||||
|
|
||||||
|
// save the counts (in correct order)
|
||||||
|
Minibase.BufferManager.flushAllPages();
|
||||||
|
data.reads = Minibase.DiskManager.getReadCount();
|
||||||
|
data.writes = Minibase.DiskManager.getWriteCount();
|
||||||
|
data.allocs = Minibase.DiskManager.getAllocCount();
|
||||||
|
data.pinned = BUF_SIZE - Minibase.BufferManager.getNumUnpinned();
|
||||||
|
|
||||||
|
} // protected void saveCounts(String desc)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the performance counters (i.e. for the current test).
|
||||||
|
*/
|
||||||
|
protected void printCounters() {
|
||||||
|
|
||||||
|
CountData data = counts.get(counts.size() - 1);
|
||||||
|
System.out.println();
|
||||||
|
Minibase.BufferManager.flushAllPages();
|
||||||
|
System.out.println(" *** Number of reads: "
|
||||||
|
+ (Minibase.DiskManager.getReadCount() - data.reads));
|
||||||
|
System.out.println(" *** Number of writes: "
|
||||||
|
+ (Minibase.DiskManager.getWriteCount() - data.writes));
|
||||||
|
System.out.println(" *** Net total pages: "
|
||||||
|
+ (Minibase.DiskManager.getAllocCount() - data.allocs));
|
||||||
|
int numbufs = Minibase.BufferManager.getNumBuffers();
|
||||||
|
System.out
|
||||||
|
.println(" *** Remaining Pinned: "
|
||||||
|
+ (numbufs - Minibase.BufferManager.getNumUnpinned()) + " / "
|
||||||
|
+ numbufs);
|
||||||
|
|
||||||
|
} // protected void printCounters()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints the complete history of the performance counters.
|
||||||
|
*
|
||||||
|
* @param sepcnt how many lines to print before each separator
|
||||||
|
*/
|
||||||
|
protected void printSummary(int sepcnt) {
|
||||||
|
|
||||||
|
System.out.println();
|
||||||
|
String seperator = "--------------------------------------";
|
||||||
|
System.out.println(seperator);
|
||||||
|
System.out.println("\tReads\tWrites\tAllocs\tPinned");
|
||||||
|
int size = counts.size();
|
||||||
|
for (int i = 1; i < size; i += 2) {
|
||||||
|
|
||||||
|
if (i % (sepcnt * 2) == 1) {
|
||||||
|
System.out.println(seperator);
|
||||||
|
}
|
||||||
|
|
||||||
|
CountData before = counts.get(i - 1);
|
||||||
|
CountData after = counts.get(i);
|
||||||
|
System.out.print(after.desc);
|
||||||
|
|
||||||
|
System.out.print("\t" + (after.reads - before.reads));
|
||||||
|
System.out.print("\t" + (after.writes - before.writes));
|
||||||
|
System.out.print("\t" + (after.allocs - before.allocs));
|
||||||
|
System.out.print("\t" + (after.pinned - before.pinned));
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
} // for
|
||||||
|
System.out.println(seperator);
|
||||||
|
|
||||||
|
} // protected void printSummary(int sepcnt)
|
||||||
|
|
||||||
|
} // class TestDriver implements GlobalConst
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
1, "Ahmed", "Elmagarmid", 25, 5
|
||||||
|
2, "Walid", "Aref", 20, 13
|
||||||
|
3, "Christopher", "Clifton", 18, 4
|
||||||
|
4, "Sunil", "Prabhakar", 22, 7
|
||||||
|
5, "Elisa", "Bertino", 26, 5
|
||||||
|
6, "Susanne", "Hambrusch", 23, 3
|
||||||
|
7, "David", "Eberts", 24, 8
|
||||||
|
8, "Arif", "Ghafoor", 20, 5
|
||||||
|
9, "Jeff", "Vitter", 19, 10
|
||||||
|
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "DriverId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 20,
|
||||||
|
"name": "FirstName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 2,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 20,
|
||||||
|
"name": "LastName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 3,
|
||||||
|
"type": "FLOAT",
|
||||||
|
"length": 4,
|
||||||
|
"name": "Age"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 4,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "NumSeats"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 3
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "DriverId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 20,
|
||||||
|
"name": "FirstName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 2,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 20,
|
||||||
|
"name": "LastName"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 3,
|
||||||
|
"type": "FLOAT",
|
||||||
|
"length": 4,
|
||||||
|
"name": "Age"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 4,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "NumSeats"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 3
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
1, "Purdue1"
|
||||||
|
2, "Purdue2"
|
||||||
|
3, "Purdue3"
|
||||||
|
4, "Purdue4"
|
||||||
|
5, "Purdue5"
|
||||||
|
6, "Purdue6"
|
||||||
|
7, "Purdue7"
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "GroupId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "GroupName"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
1," ""Purdue1"""
|
||||||
|
2," ""Purdue2"""
|
||||||
|
3," ""Purdue3"""
|
||||||
|
4," ""Purdue4"""
|
||||||
|
5," ""Purdue5"""
|
||||||
|
6," ""Purdue6"""
|
||||||
|
7," ""Purdue7"""
|
||||||
|
8," ""Purdue8"""
|
||||||
|
9," ""Purdue9"""
|
||||||
|
10," ""Purdue10"""
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "GroupId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "GroupName"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
3, 5, "2/10/2006", "2/13/2006"
|
||||||
|
1, 2, "2/12/2006", "2/14/2006"
|
||||||
|
9, 1, "2/15/2006", "2/15/2006"
|
||||||
|
5, 7, "2/14/2006", "2/18/2006"
|
||||||
|
1, 3, "2/15/2006", "2/16/2006"
|
||||||
|
2, 6, "2/17/2006", "2/20/2006"
|
||||||
|
3, 4, "2/18/2006", "2/19/2006"
|
||||||
|
4, 1, "2/19/2006", "2/19/2006"
|
||||||
|
2, 7, "2/18/2006", "2/23/2006"
|
||||||
|
8, 5, "2/20/2006", "2/22/2006"
|
||||||
|
3, 2, "2/24/2006", "2/26/2006"
|
||||||
|
6, 6, "2/25/2006", "2/26/2006"
|
||||||
|
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "DriverId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "GroupId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 2,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "FromDate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 3,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "ToDate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 1
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"no": 0,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "DriverId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 1,
|
||||||
|
"type": "INTEGER",
|
||||||
|
"length": 4,
|
||||||
|
"name": "GroupId"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 2,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "FromDate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no": 3,
|
||||||
|
"type": "STRING",
|
||||||
|
"length": 10,
|
||||||
|
"name": "ToDate"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"key": 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user