├── .gitignore ├── README.md ├── build.xml ├── lab1-writeup.txt ├── lab2-writeup.txt ├── lab3-writeup.txt ├── lab4-writeup.txt ├── lab5-writeup.txt ├── lab6-writeup.txt ├── lib ├── README ├── ant-contrib-1.0b3.jar ├── jline-0.9.94.jar ├── junit-4.5.jar └── zql.jar ├── src └── java │ └── simpledb │ ├── AbstractDbFileIterator.java │ ├── Aggregate.java │ ├── Aggregator.java │ ├── BTreeChecker.java │ ├── BTreeEntry.java │ ├── BTreeFile.java │ ├── BTreeFileEncoder.java │ ├── BTreeHeaderPage.java │ ├── BTreeInternalPage.java │ ├── BTreeLeafPage.java │ ├── BTreePage.java │ ├── BTreePageId.java │ ├── BTreeRootPtrPage.java │ ├── BTreeScan.java │ ├── BTreeUtility.java │ ├── BufferPool.java │ ├── Catalog.java │ ├── CostCard.java │ ├── Database.java │ ├── DbException.java │ ├── DbFile.java │ ├── DbFileIterator.java │ ├── DeadlockException.java │ ├── Debug.java │ ├── Delete.java │ ├── Field.java │ ├── Filter.java │ ├── HashEquiJoin.java │ ├── HeapFile.java │ ├── HeapFileEncoder.java │ ├── HeapPage.java │ ├── HeapPageId.java │ ├── IndexOpIterator.java │ ├── IndexPredicate.java │ ├── Insert.java │ ├── IntField.java │ ├── IntHistogram.java │ ├── IntegerAggregator.java │ ├── Join.java │ ├── JoinOptimizer.java │ ├── JoinPredicate.java │ ├── LogFile.java │ ├── LogicalFilterNode.java │ ├── LogicalJoinNode.java │ ├── LogicalPlan.java │ ├── LogicalScanNode.java │ ├── LogicalSelectListNode.java │ ├── LogicalSubplanJoinNode.java │ ├── OpIterator.java │ ├── Operator.java │ ├── OperatorCardinality.java │ ├── OrderBy.java │ ├── Page.java │ ├── PageId.java │ ├── Parser.java │ ├── ParsingException.java │ ├── Permissions.java │ ├── PlanCache.java │ ├── Predicate.java │ ├── Project.java │ ├── Query.java │ ├── QueryPlanVisualizer.java │ ├── RecordId.java │ ├── SeqScan.java │ ├── SimpleDb.java │ ├── StringAggregator.java │ ├── StringField.java │ ├── StringHistogram.java │ ├── TableStats.java │ ├── Transaction.java │ ├── TransactionAbortedException.java │ ├── TransactionId.java │ ├── Tuple.java │ ├── TupleDesc.java │ ├── TupleIterator.java │ ├── Type.java │ └── Utility.java ├── test └── simpledb │ ├── AggregateTest.java │ ├── BTreeDeadlockTest.java │ ├── BTreeFileDeleteTest.java │ ├── BTreeFileInsertTest.java │ ├── BTreeFileReadTest.java │ ├── BTreeHeaderPageTest.java │ ├── BTreeInternalPageTest.java │ ├── BTreeLeafPageTest.java │ ├── BTreeNextKeyLockingTest.java │ ├── BTreePageIdTest.java │ ├── BTreeRootPtrPageTest.java │ ├── BufferPoolWriteTest.java │ ├── CatalogTest.java │ ├── DeadlockTest.java │ ├── FilterTest.java │ ├── HeapFileReadTest.java │ ├── HeapFileWriteTest.java │ ├── HeapPageIdTest.java │ ├── HeapPageReadTest.java │ ├── HeapPageWriteTest.java │ ├── InsertTest.java │ ├── IntHistogramTest.java │ ├── IntegerAggregatorTest.java │ ├── JoinOptimizerTest.java │ ├── JoinPredicateTest.java │ ├── JoinTest.java │ ├── LockingTest.java │ ├── PredicateTest.java │ ├── RecordIdTest.java │ ├── StringAggregatorTest.java │ ├── TableStatsTest.java │ ├── TestUtil.java │ ├── TransactionTest.java │ ├── TupleDescTest.java │ ├── TupleTest.java │ └── systemtest │ ├── AbortEvictionTest.java │ ├── AggregateTest.java │ ├── BTreeFileDeleteTest.java │ ├── BTreeFileInsertTest.java │ ├── BTreeScanTest.java │ ├── BTreeTest.java │ ├── DeleteTest.java │ ├── EvictionTest.java │ ├── FilterBase.java │ ├── FilterTest.java │ ├── InsertTest.java │ ├── JoinTest.java │ ├── LogTest.java │ ├── QueryTest.java │ ├── ScanTest.java │ ├── SimpleDbTestBase.java │ ├── SystemTestUtil.java │ └── TransactionTest.java ├── turnInLab1.sh ├── turnInLab2.sh ├── turnInLab3.sh ├── turnInLab4.sh ├── turnInLab5.sh └── turnInLab6.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | bin/ 4 | dist/ 5 | log 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simple-db 2 | ========= 3 | 4 | Code for all 6.830 labs will be available in this repo. Once you have set up your class repo, you pull lab code from here. 5 | 6 | Directions for Repo Setup 7 | ------------------------- 8 | 9 | Directions can be [here](https://github.com/MIT-DB-Class/course-info-2018) 10 | 11 | Lab Submission 12 | ----- 13 | 14 | Instructions for labs (including how to submit answers) are [here](https://github.com/MIT-DB-Class/course-info-2018) 15 | 16 | -------------------------------------------------------------------------------- /lab1-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab1-writeup.txt -------------------------------------------------------------------------------- /lab2-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab2-writeup.txt -------------------------------------------------------------------------------- /lab3-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab3-writeup.txt -------------------------------------------------------------------------------- /lab4-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab4-writeup.txt -------------------------------------------------------------------------------- /lab5-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab5-writeup.txt -------------------------------------------------------------------------------- /lab6-writeup.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lab6-writeup.txt -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | junit-4.5.jar 2 | * http://junit.sourceforge.net/ 3 | * CPL (free for all use) 4 | 5 | zql.jar 6 | * http://www.gibello.com/code/zql/ 7 | * Free for non-commercial use 8 | 9 | JLine 10 | * http://jline.sourceforge.net/ 11 | * BSD (free for all use) 12 | 13 | mina-core-2.0.4.jar 14 | mina-filter-compression-2.0.4.jar 15 | * http://mina.apache.org/ 16 | * Apache License v2.0 (free for all use) 17 | 18 | slf4j-api-1.6.1.jar 19 | slf4j-log4j12-1.6.1.jar 20 | * http://www.slf4j.org/license.html 21 | * MIT license (free for all use) 22 | 23 | jzlib-1.0.7.jar 24 | * http://www.jcraft.com/jzlib/ 25 | * BSD (free for all use) 26 | 27 | javassist-3.16.1-GA.jar 28 | * http://www.javassist.org/ 29 | * MPL v1.1, LGPL and Apache License 30 | 31 | ant-contrib-1.0b3.jar 32 | * http://ant-contrib.sourceforge.net/ 33 | * Apache Software License 34 | 35 | log4j-1.2.17.jar 36 | * logging.apache.org/log4j/1.2/ 37 | * Apache Software license 2.0 38 | 39 | -------------------------------------------------------------------------------- /lib/ant-contrib-1.0b3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lib/ant-contrib-1.0b3.jar -------------------------------------------------------------------------------- /lib/jline-0.9.94.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lib/jline-0.9.94.jar -------------------------------------------------------------------------------- /lib/junit-4.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lib/junit-4.5.jar -------------------------------------------------------------------------------- /lib/zql.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MIT-DB-Class/simple-db-hw/1831d8f9cc28a2a36b0d3806fb164d93278e414a/lib/zql.jar -------------------------------------------------------------------------------- /src/java/simpledb/AbstractDbFileIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | /** Helper for implementing DbFileIterators. Handles hasNext()/next() logic. */ 6 | public abstract class AbstractDbFileIterator implements DbFileIterator { 7 | 8 | public boolean hasNext() throws DbException, TransactionAbortedException { 9 | if (next == null) next = readNext(); 10 | return next != null; 11 | } 12 | 13 | public Tuple next() throws DbException, TransactionAbortedException, 14 | NoSuchElementException { 15 | if (next == null) { 16 | next = readNext(); 17 | if (next == null) throw new NoSuchElementException(); 18 | } 19 | 20 | Tuple result = next; 21 | next = null; 22 | return result; 23 | } 24 | 25 | /** If subclasses override this, they should call super.close(). */ 26 | public void close() { 27 | // Ensures that a future call to next() will fail 28 | next = null; 29 | } 30 | 31 | /** Reads the next tuple from the underlying source. 32 | @return the next Tuple in the iterator, null if the iteration is finished. */ 33 | protected abstract Tuple readNext() throws DbException, TransactionAbortedException; 34 | 35 | private Tuple next = null; 36 | } 37 | -------------------------------------------------------------------------------- /src/java/simpledb/Aggregate.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * The Aggregation operator that computes an aggregate (e.g., sum, avg, max, 7 | * min). Note that we only support aggregates over a single column, grouped by a 8 | * single column. 9 | */ 10 | public class Aggregate extends Operator { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | /** 15 | * Constructor. 16 | * 17 | * Implementation hint: depending on the type of afield, you will want to 18 | * construct an {@link IntegerAggregator} or {@link StringAggregator} to help 19 | * you with your implementation of readNext(). 20 | * 21 | * 22 | * @param child 23 | * The OpIterator that is feeding us tuples. 24 | * @param afield 25 | * The column over which we are computing an aggregate. 26 | * @param gfield 27 | * The column over which we are grouping the result, or -1 if 28 | * there is no grouping 29 | * @param aop 30 | * The aggregation operator to use 31 | */ 32 | public Aggregate(OpIterator child, int afield, int gfield, Aggregator.Op aop) { 33 | // some code goes here 34 | } 35 | 36 | /** 37 | * @return If this aggregate is accompanied by a groupby, return the groupby 38 | * field index in the INPUT tuples. If not, return 39 | * {@link simpledb.Aggregator#NO_GROUPING} 40 | * */ 41 | public int groupField() { 42 | // some code goes here 43 | return -1; 44 | } 45 | 46 | /** 47 | * @return If this aggregate is accompanied by a group by, return the name 48 | * of the groupby field in the OUTPUT tuples. If not, return 49 | * null; 50 | * */ 51 | public String groupFieldName() { 52 | // some code goes here 53 | return null; 54 | } 55 | 56 | /** 57 | * @return the aggregate field 58 | * */ 59 | public int aggregateField() { 60 | // some code goes here 61 | return -1; 62 | } 63 | 64 | /** 65 | * @return return the name of the aggregate field in the OUTPUT 66 | * tuples 67 | * */ 68 | public String aggregateFieldName() { 69 | // some code goes here 70 | return null; 71 | } 72 | 73 | /** 74 | * @return return the aggregate operator 75 | * */ 76 | public Aggregator.Op aggregateOp() { 77 | // some code goes here 78 | return null; 79 | } 80 | 81 | public static String nameOfAggregatorOp(Aggregator.Op aop) { 82 | return aop.toString(); 83 | } 84 | 85 | public void open() throws NoSuchElementException, DbException, 86 | TransactionAbortedException { 87 | // some code goes here 88 | } 89 | 90 | /** 91 | * Returns the next tuple. If there is a group by field, then the first 92 | * field is the field by which we are grouping, and the second field is the 93 | * result of computing the aggregate. If there is no group by field, then 94 | * the result tuple should contain one field representing the result of the 95 | * aggregate. Should return null if there are no more tuples. 96 | */ 97 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 98 | // some code goes here 99 | return null; 100 | } 101 | 102 | public void rewind() throws DbException, TransactionAbortedException { 103 | // some code goes here 104 | } 105 | 106 | /** 107 | * Returns the TupleDesc of this Aggregate. If there is no group by field, 108 | * this will have one field - the aggregate column. If there is a group by 109 | * field, the first field will be the group by field, and the second will be 110 | * the aggregate value column. 111 | * 112 | * The name of an aggregate column should be informative. For example: 113 | * "aggName(aop) (child_td.getFieldName(afield))" where aop and afield are 114 | * given in the constructor, and child_td is the TupleDesc of the child 115 | * iterator. 116 | */ 117 | public TupleDesc getTupleDesc() { 118 | // some code goes here 119 | return null; 120 | } 121 | 122 | public void close() { 123 | // some code goes here 124 | } 125 | 126 | @Override 127 | public OpIterator[] getChildren() { 128 | // some code goes here 129 | return null; 130 | } 131 | 132 | @Override 133 | public void setChildren(OpIterator[] children) { 134 | // some code goes here 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/java/simpledb/Aggregator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * The common interface for any class that can compute an aggregate over a 7 | * list of Tuples. 8 | */ 9 | public interface Aggregator extends Serializable { 10 | static final int NO_GROUPING = -1; 11 | 12 | /** 13 | * SUM_COUNT and SC_AVG will 14 | * only be used in lab7, you are not required 15 | * to implement them until then. 16 | * */ 17 | public enum Op implements Serializable { 18 | MIN, MAX, SUM, AVG, COUNT, 19 | /** 20 | * SUM_COUNT: compute sum and count simultaneously, will be 21 | * needed to compute distributed avg in lab7. 22 | * */ 23 | SUM_COUNT, 24 | /** 25 | * SC_AVG: compute the avg of a set of SUM_COUNT tuples, 26 | * will be used to compute distributed avg in lab7. 27 | * */ 28 | SC_AVG; 29 | 30 | /** 31 | * Interface to access operations by a string containing an integer 32 | * index for command-line convenience. 33 | * 34 | * @param s a string containing a valid integer Op index 35 | */ 36 | public static Op getOp(String s) { 37 | return getOp(Integer.parseInt(s)); 38 | } 39 | 40 | /** 41 | * Interface to access operations by integer value for command-line 42 | * convenience. 43 | * 44 | * @param i a valid integer Op index 45 | */ 46 | public static Op getOp(int i) { 47 | return values()[i]; 48 | } 49 | 50 | public String toString() 51 | { 52 | if (this==MIN) 53 | return "min"; 54 | if (this==MAX) 55 | return "max"; 56 | if (this==SUM) 57 | return "sum"; 58 | if (this==SUM_COUNT) 59 | return "sum_count"; 60 | if (this==AVG) 61 | return "avg"; 62 | if (this==COUNT) 63 | return "count"; 64 | if (this==SC_AVG) 65 | return "sc_avg"; 66 | throw new IllegalStateException("impossible to reach here"); 67 | } 68 | } 69 | 70 | /** 71 | * Merge a new tuple into the aggregate for a distinct group value; 72 | * creates a new group aggregate result if the group value has not yet 73 | * been encountered. 74 | * 75 | * @param tup the Tuple containing an aggregate field and a group-by field 76 | */ 77 | public void mergeTupleIntoGroup(Tuple tup); 78 | 79 | /** 80 | * Create a OpIterator over group aggregate results. 81 | * @see simpledb.TupleIterator for a possible helper 82 | */ 83 | public OpIterator iterator(); 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/java/simpledb/BTreeChecker.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Iterator; 6 | 7 | /** 8 | * Created by orm on 10/7/15. 9 | */ 10 | public class BTreeChecker { 11 | 12 | /** 13 | * This class is only used for error-checking code. 14 | */ 15 | static class SubtreeSummary { 16 | public int depth; 17 | public BTreePageId ptrLeft; 18 | public BTreePageId leftmostId; 19 | public BTreePageId ptrRight; 20 | public BTreePageId rightmostId; 21 | 22 | SubtreeSummary() {} 23 | 24 | SubtreeSummary(BTreeLeafPage base, int depth) { 25 | this.depth = depth; 26 | 27 | this.leftmostId = base.getId(); 28 | this.rightmostId = base.getId(); 29 | 30 | this.ptrLeft = base.getLeftSiblingId(); 31 | this.ptrRight = base.getRightSiblingId(); 32 | } 33 | 34 | static SubtreeSummary checkAndMerge(SubtreeSummary accleft, SubtreeSummary right) { 35 | assert(accleft.depth == right.depth); 36 | assert(accleft.ptrRight.equals(right.leftmostId)); 37 | assert(accleft.rightmostId.equals(right.ptrLeft)); 38 | 39 | SubtreeSummary ans = new SubtreeSummary(); 40 | ans.depth = accleft.depth; 41 | 42 | ans.ptrLeft = accleft.ptrLeft; 43 | ans.leftmostId = accleft.leftmostId; 44 | 45 | ans.ptrRight = right.ptrRight; 46 | ans.rightmostId = right.rightmostId; 47 | return ans; 48 | } 49 | } 50 | 51 | /** 52 | * checks the integrity of the tree: 53 | * 1) parent pointers. 54 | * 2) sibling pointers. 55 | * 3) range invariants. 56 | * 4) record to page pointers. 57 | * 5) occupancy invariants. (if enabled) 58 | */ 59 | public static void checkRep(BTreeFile bt, TransactionId tid, HashMap dirtypages, 60 | boolean checkOccupancy) throws 61 | DbException, IOException, TransactionAbortedException { 62 | BTreeRootPtrPage rtptr = bt.getRootPtrPage(tid, dirtypages); 63 | 64 | if (rtptr.getRootId() == null) { // non existent root is a legal state. 65 | return; 66 | } else { 67 | SubtreeSummary res = checkSubTree(bt, tid, dirtypages, 68 | rtptr.getRootId(), null, null, rtptr.getId(), checkOccupancy, 0); 69 | assert (res.ptrLeft == null); 70 | assert (res.ptrRight == null); 71 | } 72 | } 73 | 74 | static SubtreeSummary checkSubTree(BTreeFile bt, TransactionId tid, HashMap dirtypages, 75 | BTreePageId pageId, Field lowerBound, Field upperBound, 76 | BTreePageId parentId, boolean checkOccupancy, int depth) throws 77 | TransactionAbortedException, DbException { 78 | BTreePage page = (BTreePage )bt.getPage(tid, dirtypages, pageId, Permissions.READ_ONLY); 79 | assert(page.getParentId().equals(parentId)); 80 | 81 | if (page.getId().pgcateg() == BTreePageId.LEAF) { 82 | BTreeLeafPage bpage = (BTreeLeafPage) page; 83 | bpage.checkRep(bt.keyField(), lowerBound, upperBound, checkOccupancy, depth); 84 | return new SubtreeSummary(bpage, depth); 85 | } else if (page.getId().pgcateg() == BTreePageId.INTERNAL) { 86 | 87 | BTreeInternalPage ipage = (BTreeInternalPage) page; 88 | ipage.checkRep(lowerBound, upperBound, checkOccupancy, depth); 89 | 90 | SubtreeSummary acc = null; 91 | BTreeEntry prev = null; 92 | Iterator it = ipage.iterator(); 93 | 94 | prev = it.next(); 95 | { // init acc and prev. 96 | acc = checkSubTree(bt, tid, dirtypages, prev.getLeftChild(), lowerBound, prev.getKey(), ipage.getId(), 97 | checkOccupancy, depth + 1); 98 | lowerBound = prev.getKey(); 99 | } 100 | 101 | assert(acc != null); 102 | BTreeEntry curr = prev; // for one entry case. 103 | while (it.hasNext()) { 104 | curr = it.next(); 105 | SubtreeSummary currentSubTreeResult = 106 | checkSubTree(bt, tid, dirtypages, curr.getLeftChild(), lowerBound, curr.getKey(), ipage.getId(), 107 | checkOccupancy, depth + 1); 108 | acc = SubtreeSummary.checkAndMerge(acc, currentSubTreeResult); 109 | 110 | // need to move stuff for next iter: 111 | lowerBound = curr.getKey(); 112 | } 113 | 114 | SubtreeSummary lastRight = checkSubTree(bt, tid, dirtypages, curr.getRightChild(), lowerBound, upperBound, 115 | ipage.getId(), checkOccupancy, depth + 1); 116 | acc = SubtreeSummary.checkAndMerge(acc, lastRight); 117 | 118 | return acc; 119 | } else { 120 | assert(false); // no other page types allowed inside the tree. 121 | return null; 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /src/java/simpledb/BTreeEntry.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Each instance of BTreeEntry stores one key and two child page ids. It is used 7 | * by BTreeInternalPage as an abstraction to iterate through the entries stored inside. 8 | * All of the entries or tuples in the left child page should be less than or equal to 9 | * the key, and all of the entries or tuples in the right child page should be greater 10 | * than or equal to the key. 11 | * 12 | * Note that updating a BTreeEntry does not actually change the data stored on the page 13 | * identified by its recordId. After updating a BTreeEntry object, you must call 14 | * BTreeInternalPage.updateEntry() in order for the changes to take effect. 15 | * 16 | * @see BTreeInternalPage 17 | * @see BTreeInternalPage#updateEntry(BTreeEntry) 18 | * 19 | */ 20 | public class BTreeEntry implements Serializable { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | /** 25 | * The key of this entry 26 | * */ 27 | private Field key; 28 | 29 | /** 30 | * The left child page id 31 | * */ 32 | private BTreePageId leftChild; 33 | 34 | /** 35 | * The right child page id 36 | * */ 37 | private BTreePageId rightChild; 38 | 39 | /** 40 | * The record id of this entry 41 | * */ 42 | private RecordId rid; // null if not stored on any page 43 | 44 | /** 45 | * Constructor to create a new BTreeEntry 46 | * @param key - the key 47 | * @param leftChild - page id of the left child 48 | * @param rightChild - page id of the right child 49 | */ 50 | public BTreeEntry(Field key, BTreePageId leftChild, BTreePageId rightChild) { 51 | this.key = key; 52 | this.leftChild = leftChild; 53 | this.rightChild = rightChild; 54 | } 55 | 56 | /** 57 | * @return the key 58 | */ 59 | public Field getKey() { 60 | return key; 61 | } 62 | 63 | /** 64 | * @return the left child page id 65 | */ 66 | public BTreePageId getLeftChild() { 67 | return leftChild; 68 | } 69 | 70 | /** 71 | * @return the right child page id 72 | */ 73 | public BTreePageId getRightChild() { 74 | return rightChild; 75 | } 76 | 77 | /** 78 | * @return the record id of this entry, representing the location of this entry 79 | * in a BTreeFile. May be null if this entry is not stored on any page in the file 80 | */ 81 | public RecordId getRecordId() { 82 | return rid; 83 | } 84 | 85 | /** 86 | * Set the key for this entry. Note that updating a BTreeEntry does not 87 | * actually change the data stored on the page identified by its recordId. After 88 | * calling this method, you must call BTreeInternalPage.updateEntry() in order for 89 | * it to take effect. 90 | * @param key - the new key 91 | * @see BTreeInternalPage#updateEntry(BTreeEntry) 92 | */ 93 | public void setKey(Field key) { 94 | this.key = key; 95 | } 96 | 97 | /** 98 | * Set the left child id for this entry. Note that updating a BTreeEntry does not 99 | * actually change the data stored on the page identified by its recordId. After 100 | * calling this method, you must call BTreeInternalPage.updateEntry() in order for 101 | * it to take effect. 102 | * @param leftChild - the new left child 103 | * @see BTreeInternalPage#updateEntry(BTreeEntry) 104 | */ 105 | public void setLeftChild(BTreePageId leftChild) { 106 | this.leftChild = leftChild; 107 | } 108 | 109 | /** 110 | * Set the right child id for this entry. Note that updating a BTreeEntry does not 111 | * actually change the data stored on the page identified by its recordId. After 112 | * calling this method, you must call BTreeInternalPage.updateEntry() in order for 113 | * it to take effect. 114 | * @param rightChild - the new right child 115 | * @see BTreeInternalPage#updateEntry(BTreeEntry) 116 | */ 117 | public void setRightChild(BTreePageId rightChild) { 118 | this.rightChild = rightChild; 119 | } 120 | 121 | /** 122 | * set the record id for this entry 123 | * @param rid - the new record id 124 | */ 125 | public void setRecordId(RecordId rid) { 126 | this.rid = rid; 127 | } 128 | 129 | /** 130 | * Prints a representation of this BTreeEntry 131 | */ 132 | public String toString() { 133 | return "[" + leftChild.getPageNumber() + "|" + key + "|" + rightChild.getPageNumber() + "]"; 134 | } 135 | 136 | } 137 | 138 | -------------------------------------------------------------------------------- /src/java/simpledb/BTreePage.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Each instance of BTreeInternalPage stores data for one page of a BTreeFile and 7 | * implements the Page interface that is used by BufferPool. 8 | * 9 | * @see BTreeFile 10 | * @see BufferPool 11 | * 12 | */ 13 | public abstract class BTreePage implements Page { 14 | protected volatile boolean dirty = false; 15 | protected volatile TransactionId dirtier = null; 16 | 17 | protected final static int INDEX_SIZE = Type.INT_TYPE.getLen(); 18 | 19 | protected final BTreePageId pid; 20 | protected final TupleDesc td; 21 | protected final int keyField; 22 | 23 | protected int parent; // parent is always internal node or 0 for root node 24 | protected byte[] oldData; 25 | protected final Byte oldDataLock=new Byte((byte)0); 26 | 27 | /** 28 | * Create a BTreeInternalPage from a set of bytes of data read from disk. 29 | * The format of a BTreeInternalPage is a set of header bytes indicating 30 | * the slots of the page that are in use, some number of entry slots, and extra 31 | * bytes for the parent pointer, one extra child pointer (a node with m entries 32 | * has m+1 pointers to children), and the category of all child pages (either 33 | * leaf or internal). 34 | * Specifically, the number of entries is equal to:

35 | * floor((BufferPool.getPageSize()*8 - extra bytes*8) / (entry size * 8 + 1)) 36 | *

where entry size is the size of entries in this index node 37 | * (key + child pointer), which can be determined via the key field and 38 | * {@link Catalog#getTupleDesc}. 39 | * The number of 8-bit header words is equal to: 40 | *

41 | * ceiling((no. entry slots + 1) / 8) 42 | *

43 | * @see Database#getCatalog 44 | * @see Catalog#getTupleDesc 45 | * @see BufferPool#getPageSize() 46 | * 47 | * @param id - the id of this page 48 | * @param data - the raw data of this page 49 | * @param key - the field which the index is keyed on 50 | */ 51 | public BTreePage(BTreePageId id, int key) throws IOException { 52 | this.pid = id; 53 | this.keyField = key; 54 | this.td = Database.getCatalog().getTupleDesc(id.getTableId()); 55 | } 56 | 57 | /** 58 | * @return the PageId associated with this page. 59 | */ 60 | public BTreePageId getId() { 61 | return pid; 62 | } 63 | 64 | /** 65 | * Static method to generate a byte array corresponding to an empty 66 | * BTreePage. 67 | * Used to add new, empty pages to the file. Passing the results of 68 | * this method to the BTreeInternalPage or BTreeLeafPage constructor will create a BTreePage with 69 | * no valid entries in it. 70 | * 71 | * @return The returned ByteArray. 72 | */ 73 | public static byte[] createEmptyPageData() { 74 | int len = BufferPool.getPageSize(); 75 | return new byte[len]; //all 0 76 | } 77 | 78 | /** 79 | * Get the parent id of this page 80 | * @return the parent id 81 | */ 82 | public BTreePageId getParentId() { 83 | if(parent == 0) { 84 | return BTreeRootPtrPage.getId(pid.getTableId()); 85 | } 86 | return new BTreePageId(pid.getTableId(), parent, BTreePageId.INTERNAL); 87 | } 88 | 89 | /** 90 | * Set the parent id 91 | * @param id - the id of the parent of this page 92 | * @throws DbException if the id is not valid 93 | */ 94 | public void setParentId(BTreePageId id) throws DbException { 95 | if(id == null) { 96 | throw new DbException("parent id must not be null"); 97 | } 98 | if(id.getTableId() != pid.getTableId()) { 99 | throw new DbException("table id mismatch in setParentId"); 100 | } 101 | if(id.pgcateg() != BTreePageId.INTERNAL && id.pgcateg() != BTreePageId.ROOT_PTR) { 102 | throw new DbException("parent must be an internal node or root pointer"); 103 | } 104 | if(id.pgcateg() == BTreePageId.ROOT_PTR) { 105 | parent = 0; 106 | } 107 | else { 108 | parent = id.getPageNumber(); 109 | } 110 | } 111 | 112 | /** 113 | * Marks this page as dirty/not dirty and record that transaction 114 | * that did the dirtying 115 | */ 116 | public void markDirty(boolean dirty, TransactionId tid) { 117 | this.dirty = dirty; 118 | if (dirty) this.dirtier = tid; 119 | } 120 | 121 | /** 122 | * Returns the tid of the transaction that last dirtied this page, or null if the page is not dirty 123 | */ 124 | public TransactionId isDirty() { 125 | if (this.dirty) 126 | return this.dirtier; 127 | else 128 | return null; 129 | } 130 | 131 | /** 132 | * Returns the number of empty slots on this page. 133 | */ 134 | public abstract int getNumEmptySlots(); 135 | 136 | /** 137 | * Returns true if associated slot on this page is filled. 138 | */ 139 | public abstract boolean isSlotUsed(int i); 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/java/simpledb/BTreePageId.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** Unique identifier for BTreeInternalPage, BTreeLeafPage, BTreeHeaderPage 4 | * and BTreeRootPtrPage objects. 5 | */ 6 | public class BTreePageId implements PageId { 7 | 8 | public final static int ROOT_PTR = 0; 9 | public final static int INTERNAL = 1; 10 | public final static int LEAF = 2; 11 | public final static int HEADER = 3; 12 | 13 | private final int tableId; 14 | private final int pgNo; 15 | private int pgcateg; 16 | 17 | static public String categToString(int categ) { 18 | switch (categ) { 19 | case ROOT_PTR: 20 | return "ROOT_PTR"; 21 | case INTERNAL: 22 | return "INTERNAL"; 23 | case LEAF: 24 | return "LEAF"; 25 | case HEADER: 26 | return "HEADER"; 27 | default: 28 | throw new IllegalArgumentException("categ"); 29 | } 30 | } 31 | 32 | /** 33 | * Constructor. Create a page id structure for a specific page of a 34 | * specific table. 35 | * 36 | * @param tableId The table that is being referenced 37 | * @param pgNo The page number in that table. 38 | * @param pgcateg which kind of page it is 39 | */ 40 | public BTreePageId(int tableId, int pgNo, int pgcateg) { 41 | this.tableId = tableId; 42 | this.pgNo = pgNo; 43 | this.pgcateg = pgcateg; 44 | } 45 | 46 | /** @return the table associated with this PageId */ 47 | public int getTableId() { 48 | return tableId; 49 | } 50 | 51 | /** 52 | * @return the page number in the table getTableId() associated with 53 | * this PageId 54 | */ 55 | public int getPageNumber() { 56 | return pgNo; 57 | } 58 | 59 | /** 60 | * @return the category of this page 61 | */ 62 | public int pgcateg() { 63 | return pgcateg; 64 | } 65 | 66 | /** 67 | * @return a hash code for this page, represented by the concatenation of 68 | * the table number, page number, and pgcateg (needed if a PageId is used as a 69 | * key in a hash table in the BufferPool, for example.) 70 | * @see BufferPool 71 | */ 72 | public int hashCode() { 73 | int code = (tableId << 16) + (pgNo << 2) + pgcateg; 74 | return code; 75 | } 76 | 77 | /** 78 | * Compares one PageId to another. 79 | * 80 | * @param o The object to compare against (must be a PageId) 81 | * @return true if the objects are equal (e.g., page numbers, table 82 | * ids and pgcateg are the same) 83 | */ 84 | public boolean equals(Object o) { 85 | if (!(o instanceof BTreePageId)) 86 | return false; 87 | BTreePageId p = (BTreePageId)o; 88 | return tableId == p.tableId && pgNo == p.pgNo && pgcateg == p.pgcateg; 89 | } 90 | 91 | public String toString() { 92 | StringBuilder sb = new StringBuilder(); 93 | sb.append("(tableId: ").append(tableId) 94 | .append(", pgNo: ").append(pgNo) 95 | .append(", pgcateg: ").append(categToString(pgcateg)) 96 | .append(")"); 97 | 98 | return sb.toString(); 99 | } 100 | 101 | /** 102 | * Return a representation of this object as an array of 103 | * integers, for writing to disk. Size of returned array must contain 104 | * number of integers that corresponds to number of args to one of the 105 | * constructors. 106 | */ 107 | public int[] serialize() { 108 | int data[] = new int[3]; 109 | 110 | data[0] = tableId; 111 | data[1] = pgNo; 112 | data[2] = pgcateg; 113 | 114 | return data; 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/java/simpledb/BTreeScan.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * BTreeScan is an operator which reads tuples in sorted order 7 | * according to a predicate 8 | */ 9 | public class BTreeScan implements OpIterator { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | private boolean isOpen = false; 14 | private TransactionId tid; 15 | private TupleDesc myTd; 16 | private IndexPredicate ipred = null; 17 | private transient DbFileIterator it; 18 | private String tablename; 19 | private String alias; 20 | 21 | /** 22 | * Creates a B+ tree scan over the specified table as a part of the 23 | * specified transaction. 24 | * 25 | * @param tid 26 | * The transaction this scan is running as a part of. 27 | * @param tableid 28 | * the table to scan. 29 | * @param tableAlias 30 | * the alias of this table (needed by the parser); the returned 31 | * tupleDesc should have fields with name tableAlias.fieldName 32 | * (note: this class is not responsible for handling a case where 33 | * tableAlias or fieldName are null. It shouldn't crash if they 34 | * are, but the resulting name can be null.fieldName, 35 | * tableAlias.null, or null.null). 36 | * @param ipred 37 | * The index predicate to match. If null, the scan will return all tuples 38 | * in sorted order 39 | */ 40 | public BTreeScan(TransactionId tid, int tableid, String tableAlias, IndexPredicate ipred) { 41 | this.tid = tid; 42 | this.ipred = ipred; 43 | reset(tableid,tableAlias); 44 | } 45 | 46 | /** 47 | * @return 48 | * return the table name of the table the operator scans. This should 49 | * be the actual name of the table in the catalog of the database 50 | * */ 51 | public String getTableName() { 52 | return this.tablename; 53 | } 54 | 55 | /** 56 | * @return Return the alias of the table this operator scans. 57 | * */ 58 | public String getAlias() 59 | { 60 | return this.alias; 61 | } 62 | 63 | /** 64 | * Reset the tableid, and tableAlias of this operator. 65 | * @param tableid 66 | * the table to scan. 67 | * @param tableAlias 68 | * the alias of this table (needed by the parser); the returned 69 | * tupleDesc should have fields with name tableAlias.fieldName 70 | * (note: this class is not responsible for handling a case where 71 | * tableAlias or fieldName are null. It shouldn't crash if they 72 | * are, but the resulting name can be null.fieldName, 73 | * tableAlias.null, or null.null). 74 | */ 75 | public void reset(int tableid, String tableAlias) { 76 | this.isOpen=false; 77 | this.alias = tableAlias; 78 | this.tablename = Database.getCatalog().getTableName(tableid); 79 | if(ipred == null) { 80 | this.it = Database.getCatalog().getDatabaseFile(tableid).iterator(tid); 81 | } 82 | else { 83 | this.it = ((BTreeFile) Database.getCatalog().getDatabaseFile(tableid)).indexIterator(tid, ipred); 84 | } 85 | myTd = Database.getCatalog().getTupleDesc(tableid); 86 | String[] newNames = new String[myTd.numFields()]; 87 | Type[] newTypes = new Type[myTd.numFields()]; 88 | for (int i = 0; i < myTd.numFields(); i++) { 89 | String name = myTd.getFieldName(i); 90 | Type t = myTd.getFieldType(i); 91 | 92 | newNames[i] = tableAlias + "." + name; 93 | newTypes[i] = t; 94 | } 95 | myTd = new TupleDesc(newTypes, newNames); 96 | } 97 | 98 | public BTreeScan(TransactionId tid, int tableid, IndexPredicate ipred) { 99 | this(tid, tableid, Database.getCatalog().getTableName(tableid), ipred); 100 | } 101 | 102 | public void open() throws DbException, TransactionAbortedException { 103 | if (isOpen) 104 | throw new DbException("double open on one OpIterator."); 105 | 106 | it.open(); 107 | isOpen = true; 108 | } 109 | 110 | /** 111 | * Returns the TupleDesc with field names from the underlying BTreeFile, 112 | * prefixed with the tableAlias string from the constructor. This prefix 113 | * becomes useful when joining tables containing a field(s) with the same 114 | * name. 115 | * 116 | * @return the TupleDesc with field names from the underlying BTreeFile, 117 | * prefixed with the tableAlias string from the constructor. 118 | */ 119 | public TupleDesc getTupleDesc() { 120 | return myTd; 121 | } 122 | 123 | public boolean hasNext() throws TransactionAbortedException, DbException { 124 | if (!isOpen) 125 | throw new IllegalStateException("iterator is closed"); 126 | return it.hasNext(); 127 | } 128 | 129 | public Tuple next() throws NoSuchElementException, 130 | TransactionAbortedException, DbException { 131 | if (!isOpen) 132 | throw new IllegalStateException("iterator is closed"); 133 | 134 | return it.next(); 135 | } 136 | 137 | public void close() { 138 | it.close(); 139 | isOpen = false; 140 | } 141 | 142 | public void rewind() throws DbException, NoSuchElementException, 143 | TransactionAbortedException { 144 | close(); 145 | open(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/java/simpledb/CostCard.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.util.Vector; 3 | 4 | /** Class returned by {@link JoinOptimizer#computeCostAndCardOfSubplan} specifying the 5 | cost and cardinality of the optimal plan represented by plan. 6 | */ 7 | public class CostCard { 8 | /** The cost of the optimal subplan */ 9 | public double cost; 10 | /** The cardinality of the optimal subplan */ 11 | public int card; 12 | /** The optimal subplan */ 13 | public Vector plan; 14 | } 15 | -------------------------------------------------------------------------------- /src/java/simpledb/Database.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | import java.util.concurrent.atomic.AtomicReference; 5 | 6 | /** 7 | * Database is a class that initializes several static variables used by the 8 | * database system (the catalog, the buffer pool, and the log files, in 9 | * particular.) 10 | *

11 | * Provides a set of methods that can be used to access these variables from 12 | * anywhere. 13 | * 14 | * @Threadsafe 15 | */ 16 | public class Database { 17 | private static AtomicReference _instance = new AtomicReference(new Database()); 18 | private final Catalog _catalog; 19 | private final BufferPool _bufferpool; 20 | 21 | private final static String LOGFILENAME = "log"; 22 | private final LogFile _logfile; 23 | 24 | private Database() { 25 | _catalog = new Catalog(); 26 | _bufferpool = new BufferPool(BufferPool.DEFAULT_PAGES); 27 | LogFile tmp = null; 28 | try { 29 | tmp = new LogFile(new File(LOGFILENAME)); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | System.exit(1); 33 | } 34 | _logfile = tmp; 35 | // startControllerThread(); 36 | } 37 | 38 | /** Return the log file of the static Database instance */ 39 | public static LogFile getLogFile() { 40 | return _instance.get()._logfile; 41 | } 42 | 43 | /** Return the buffer pool of the static Database instance */ 44 | public static BufferPool getBufferPool() { 45 | return _instance.get()._bufferpool; 46 | } 47 | 48 | /** Return the catalog of the static Database instance */ 49 | public static Catalog getCatalog() { 50 | return _instance.get()._catalog; 51 | } 52 | 53 | /** 54 | * Method used for testing -- create a new instance of the buffer pool and 55 | * return it 56 | */ 57 | public static BufferPool resetBufferPool(int pages) { 58 | java.lang.reflect.Field bufferPoolF=null; 59 | try { 60 | bufferPoolF = Database.class.getDeclaredField("_bufferpool"); 61 | bufferPoolF.setAccessible(true); 62 | bufferPoolF.set(_instance.get(), new BufferPool(pages)); 63 | } catch (NoSuchFieldException e) { 64 | e.printStackTrace(); 65 | } catch (SecurityException e) { 66 | e.printStackTrace(); 67 | } catch (IllegalArgumentException e) { 68 | e.printStackTrace(); 69 | } catch (IllegalAccessException e) { 70 | e.printStackTrace(); 71 | } 72 | // _instance._bufferpool = new BufferPool(pages); 73 | return _instance.get()._bufferpool; 74 | } 75 | 76 | // reset the database, used for unit tests only. 77 | public static void reset() { 78 | _instance.set(new Database()); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/java/simpledb/DbException.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.lang.Exception; 4 | 5 | /** Generic database exception class */ 6 | public class DbException extends Exception { 7 | private static final long serialVersionUID = 1L; 8 | 9 | public DbException(String s) { 10 | super(s); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/java/simpledb/DbFile.java: -------------------------------------------------------------------------------- 1 | 2 | package simpledb; 3 | 4 | import java.util.*; 5 | import java.io.*; 6 | 7 | /** 8 | * The interface for database files on disk. Each table is represented by a 9 | * single DbFile. DbFiles can fetch pages and iterate through tuples. Each 10 | * file has a unique id used to store metadata about the table in the Catalog. 11 | * DbFiles are generally accessed through the buffer pool, rather than directly 12 | * by operators. 13 | */ 14 | public interface DbFile { 15 | /** 16 | * Read the specified page from disk. 17 | * 18 | * @throws IllegalArgumentException if the page does not exist in this file. 19 | */ 20 | public Page readPage(PageId id); 21 | 22 | /** 23 | * Push the specified page to disk. 24 | * 25 | * @param p The page to write. page.getId().pageno() specifies the offset into the file where the page should be written. 26 | * @throws IOException if the write fails 27 | * 28 | */ 29 | public void writePage(Page p) throws IOException; 30 | 31 | /** 32 | * Inserts the specified tuple to the file on behalf of transaction. 33 | * This method will acquire a lock on the affected pages of the file, and 34 | * may block until the lock can be acquired. 35 | * 36 | * @param tid The transaction performing the update 37 | * @param t The tuple to add. This tuple should be updated to reflect that 38 | * it is now stored in this file. 39 | * @return An ArrayList contain the pages that were modified 40 | * @throws DbException if the tuple cannot be added 41 | * @throws IOException if the needed file can't be read/written 42 | */ 43 | public ArrayList insertTuple(TransactionId tid, Tuple t) 44 | throws DbException, IOException, TransactionAbortedException; 45 | 46 | /** 47 | * Removes the specified tuple from the file on behalf of the specified 48 | * transaction. 49 | * This method will acquire a lock on the affected pages of the file, and 50 | * may block until the lock can be acquired. 51 | * 52 | * @param tid The transaction performing the update 53 | * @param t The tuple to delete. This tuple should be updated to reflect that 54 | * it is no longer stored on any page. 55 | * @return An ArrayList contain the pages that were modified 56 | * @throws DbException if the tuple cannot be deleted or is not a member 57 | * of the file 58 | */ 59 | public ArrayList deleteTuple(TransactionId tid, Tuple t) 60 | throws DbException, IOException, TransactionAbortedException; 61 | 62 | /** 63 | * Returns an iterator over all the tuples stored in this DbFile. The 64 | * iterator must use {@link BufferPool#getPage}, rather than 65 | * {@link #readPage} to iterate through the pages. 66 | * 67 | * @return an iterator over all the tuples stored in this DbFile. 68 | */ 69 | public DbFileIterator iterator(TransactionId tid); 70 | 71 | /** 72 | * Returns a unique ID used to identify this DbFile in the Catalog. This id 73 | * can be used to look up the table via {@link Catalog#getDatabaseFile} and 74 | * {@link Catalog#getTupleDesc}. 75 | *

76 | * Implementation note: you will need to generate this tableid somewhere, 77 | * ensure that each HeapFile has a "unique id," and that you always 78 | * return the same value for a particular HeapFile. A simple implementation 79 | * is to use the hash code of the absolute path of the file underlying 80 | * the HeapFile, i.e. f.getAbsoluteFile().hashCode(). 81 | * 82 | * @return an ID uniquely identifying this HeapFile. 83 | */ 84 | public int getId(); 85 | 86 | /** 87 | * Returns the TupleDesc of the table stored in this DbFile. 88 | * @return TupleDesc of this DbFile. 89 | */ 90 | public TupleDesc getTupleDesc(); 91 | } 92 | -------------------------------------------------------------------------------- /src/java/simpledb/DbFileIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.util.*; 3 | 4 | /** 5 | * DbFileIterator is the iterator interface that all SimpleDB Dbfile should 6 | * implement. 7 | */ 8 | public interface DbFileIterator{ 9 | /** 10 | * Opens the iterator 11 | * @throws DbException when there are problems opening/accessing the database. 12 | */ 13 | public void open() 14 | throws DbException, TransactionAbortedException; 15 | 16 | /** @return true if there are more tuples available, false if no more tuples or iterator isn't open. */ 17 | public boolean hasNext() 18 | throws DbException, TransactionAbortedException; 19 | 20 | /** 21 | * Gets the next tuple from the operator (typically implementing by reading 22 | * from a child operator or an access method). 23 | * 24 | * @return The next tuple in the iterator. 25 | * @throws NoSuchElementException if there are no more tuples 26 | */ 27 | public Tuple next() 28 | throws DbException, TransactionAbortedException, NoSuchElementException; 29 | 30 | /** 31 | * Resets the iterator to the start. 32 | * @throws DbException When rewind is unsupported. 33 | */ 34 | public void rewind() throws DbException, TransactionAbortedException; 35 | 36 | /** 37 | * Closes the iterator. 38 | */ 39 | public void close(); 40 | } 41 | -------------------------------------------------------------------------------- /src/java/simpledb/DeadlockException.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.lang.Exception; 4 | 5 | /** Exception that is thrown when a deadlock occurs. */ 6 | public class DeadlockException extends Exception { 7 | private static final long serialVersionUID = 1L; 8 | 9 | public DeadlockException() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/java/simpledb/Debug.java: -------------------------------------------------------------------------------- 1 | 2 | package simpledb; 3 | 4 | /** 5 | * Debug is a utility class that wraps println statements and allows 6 | * more or less command line output to be turned on. 7 | *

8 | * Change the value of the DEBUG_LEVEL constant using a system property: 9 | * simpledb.Debug. For example, on the command line, use -Dsimpledb.Debug=x, 10 | * or simply -Dsimpledb.Debug to enable it at level 0. 11 | * The log(level, message, ...) method will print to standard output if the 12 | * level number is less than or equal to the currently set DEBUG_LEVEL. 13 | */ 14 | 15 | public class Debug { 16 | private static final int DEBUG_LEVEL; 17 | static { 18 | String debug = System.getProperty("simpledb.Debug"); 19 | if (debug == null) { 20 | // No system property = disabled 21 | DEBUG_LEVEL = -1; 22 | } else if (debug.length() == 0) { 23 | // Empty property = level 0 24 | DEBUG_LEVEL = 0; 25 | } else { 26 | DEBUG_LEVEL = Integer.parseInt(debug); 27 | } 28 | } 29 | 30 | private static final int DEFAULT_LEVEL = 0; 31 | 32 | /** Log message if the log level >= level. Uses printf. */ 33 | public static void log(int level, String message, Object... args) { 34 | if (isEnabled(level)) { 35 | System.out.printf(message, args); 36 | System.out.println(); 37 | } 38 | } 39 | 40 | /** @return true if level is being logged. */ 41 | public static boolean isEnabled(int level) { 42 | return level <= DEBUG_LEVEL; 43 | } 44 | 45 | /** @return true if the default level is being logged. */ 46 | public static boolean isEnabled() { 47 | return isEnabled(DEFAULT_LEVEL); 48 | } 49 | 50 | /** Logs message at the default log level. */ 51 | public static void log(String message, Object... args) { 52 | log(DEFAULT_LEVEL, message, args); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/java/simpledb/Delete.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * The delete operator. Delete reads tuples from its child operator and removes 7 | * them from the table they belong to. 8 | */ 9 | public class Delete extends Operator { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | /** 14 | * Constructor specifying the transaction that this delete belongs to as 15 | * well as the child to read from. 16 | * 17 | * @param t 18 | * The transaction this delete runs in 19 | * @param child 20 | * The child operator from which to read tuples for deletion 21 | */ 22 | public Delete(TransactionId t, OpIterator child) { 23 | // some code goes here 24 | } 25 | 26 | public TupleDesc getTupleDesc() { 27 | // some code goes here 28 | return null; 29 | } 30 | 31 | public void open() throws DbException, TransactionAbortedException { 32 | // some code goes here 33 | } 34 | 35 | public void close() { 36 | // some code goes here 37 | } 38 | 39 | public void rewind() throws DbException, TransactionAbortedException { 40 | // some code goes here 41 | } 42 | 43 | /** 44 | * Deletes tuples as they are read from the child operator. Deletes are 45 | * processed via the buffer pool (which can be accessed via the 46 | * Database.getBufferPool() method. 47 | * 48 | * @return A 1-field tuple containing the number of deleted records. 49 | * @see Database#getBufferPool 50 | * @see BufferPool#deleteTuple 51 | */ 52 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 53 | // some code goes here 54 | return null; 55 | } 56 | 57 | @Override 58 | public OpIterator[] getChildren() { 59 | // some code goes here 60 | return null; 61 | } 62 | 63 | @Override 64 | public void setChildren(OpIterator[] children) { 65 | // some code goes here 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/java/simpledb/Field.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Interface for values of fields in tuples in SimpleDB. 7 | */ 8 | public interface Field extends Serializable{ 9 | /** 10 | * Write the bytes representing this field to the specified 11 | * DataOutputStream. 12 | * @see DataOutputStream 13 | * @param dos The DataOutputStream to write to. 14 | */ 15 | void serialize(DataOutputStream dos) throws IOException; 16 | 17 | /** 18 | * Compare the value of this field object to the passed in value. 19 | * @param op The operator 20 | * @param value The value to compare this Field to 21 | * @return Whether or not the comparison yields true. 22 | */ 23 | public boolean compare(Predicate.Op op, Field value); 24 | 25 | /** 26 | * Returns the type of this field (see {@link Type#INT_TYPE} or {@link Type#STRING_TYPE} 27 | * @return type of this field 28 | */ 29 | public Type getType(); 30 | 31 | /** 32 | * Hash code. 33 | * Different Field objects representing the same value should probably 34 | * return the same hashCode. 35 | */ 36 | public int hashCode(); 37 | public boolean equals(Object field); 38 | 39 | public String toString(); 40 | } 41 | -------------------------------------------------------------------------------- /src/java/simpledb/Filter.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Filter is an operator that implements a relational select. 7 | */ 8 | public class Filter extends Operator { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | /** 13 | * Constructor accepts a predicate to apply and a child operator to read 14 | * tuples to filter from. 15 | * 16 | * @param p 17 | * The predicate to filter tuples with 18 | * @param child 19 | * The child operator 20 | */ 21 | public Filter(Predicate p, OpIterator child) { 22 | // some code goes here 23 | } 24 | 25 | public Predicate getPredicate() { 26 | // some code goes here 27 | return null; 28 | } 29 | 30 | public TupleDesc getTupleDesc() { 31 | // some code goes here 32 | return null; 33 | } 34 | 35 | public void open() throws DbException, NoSuchElementException, 36 | TransactionAbortedException { 37 | // some code goes here 38 | } 39 | 40 | public void close() { 41 | // some code goes here 42 | } 43 | 44 | public void rewind() throws DbException, TransactionAbortedException { 45 | // some code goes here 46 | } 47 | 48 | /** 49 | * AbstractDbIterator.readNext implementation. Iterates over tuples from the 50 | * child operator, applying the predicate to them and returning those that 51 | * pass the predicate (i.e. for which the Predicate.filter() returns true.) 52 | * 53 | * @return The next tuple that passes the filter, or null if there are no 54 | * more tuples 55 | * @see Predicate#filter 56 | */ 57 | protected Tuple fetchNext() throws NoSuchElementException, 58 | TransactionAbortedException, DbException { 59 | // some code goes here 60 | return null; 61 | } 62 | 63 | @Override 64 | public OpIterator[] getChildren() { 65 | // some code goes here 66 | return null; 67 | } 68 | 69 | @Override 70 | public void setChildren(OpIterator[] children) { 71 | // some code goes here 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/java/simpledb/HeapFile.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | 6 | /** 7 | * HeapFile is an implementation of a DbFile that stores a collection of tuples 8 | * in no particular order. Tuples are stored on pages, each of which is a fixed 9 | * size, and the file is simply a collection of those pages. HeapFile works 10 | * closely with HeapPage. The format of HeapPages is described in the HeapPage 11 | * constructor. 12 | * 13 | * @see simpledb.HeapPage#HeapPage 14 | * @author Sam Madden 15 | */ 16 | public class HeapFile implements DbFile { 17 | 18 | /** 19 | * Constructs a heap file backed by the specified file. 20 | * 21 | * @param f 22 | * the file that stores the on-disk backing store for this heap 23 | * file. 24 | */ 25 | public HeapFile(File f, TupleDesc td) { 26 | // some code goes here 27 | } 28 | 29 | /** 30 | * Returns the File backing this HeapFile on disk. 31 | * 32 | * @return the File backing this HeapFile on disk. 33 | */ 34 | public File getFile() { 35 | // some code goes here 36 | return null; 37 | } 38 | 39 | /** 40 | * Returns an ID uniquely identifying this HeapFile. Implementation note: 41 | * you will need to generate this tableid somewhere to ensure that each 42 | * HeapFile has a "unique id," and that you always return the same value for 43 | * a particular HeapFile. We suggest hashing the absolute file name of the 44 | * file underlying the heapfile, i.e. f.getAbsoluteFile().hashCode(). 45 | * 46 | * @return an ID uniquely identifying this HeapFile. 47 | */ 48 | public int getId() { 49 | // some code goes here 50 | throw new UnsupportedOperationException("implement this"); 51 | } 52 | 53 | /** 54 | * Returns the TupleDesc of the table stored in this DbFile. 55 | * 56 | * @return TupleDesc of this DbFile. 57 | */ 58 | public TupleDesc getTupleDesc() { 59 | // some code goes here 60 | throw new UnsupportedOperationException("implement this"); 61 | } 62 | 63 | // see DbFile.java for javadocs 64 | public Page readPage(PageId pid) { 65 | // some code goes here 66 | return null; 67 | } 68 | 69 | // see DbFile.java for javadocs 70 | public void writePage(Page page) throws IOException { 71 | // some code goes here 72 | // not necessary for lab1 73 | } 74 | 75 | /** 76 | * Returns the number of pages in this HeapFile. 77 | */ 78 | public int numPages() { 79 | // some code goes here 80 | return 0; 81 | } 82 | 83 | // see DbFile.java for javadocs 84 | public ArrayList insertTuple(TransactionId tid, Tuple t) 85 | throws DbException, IOException, TransactionAbortedException { 86 | // some code goes here 87 | return null; 88 | // not necessary for lab1 89 | } 90 | 91 | // see DbFile.java for javadocs 92 | public ArrayList deleteTuple(TransactionId tid, Tuple t) throws DbException, 93 | TransactionAbortedException { 94 | // some code goes here 95 | return null; 96 | // not necessary for lab1 97 | } 98 | 99 | // see DbFile.java for javadocs 100 | public DbFileIterator iterator(TransactionId tid) { 101 | // some code goes here 102 | return null; 103 | } 104 | 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/java/simpledb/HeapPageId.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** Unique identifier for HeapPage objects. */ 4 | public class HeapPageId implements PageId { 5 | 6 | /** 7 | * Constructor. Create a page id structure for a specific page of a 8 | * specific table. 9 | * 10 | * @param tableId The table that is being referenced 11 | * @param pgNo The page number in that table. 12 | */ 13 | public HeapPageId(int tableId, int pgNo) { 14 | // some code goes here 15 | } 16 | 17 | /** @return the table associated with this PageId */ 18 | public int getTableId() { 19 | // some code goes here 20 | return 0; 21 | } 22 | 23 | /** 24 | * @return the page number in the table getTableId() associated with 25 | * this PageId 26 | */ 27 | public int getPageNumber() { 28 | // some code goes here 29 | return 0; 30 | } 31 | 32 | /** 33 | * @return a hash code for this page, represented by the concatenation of 34 | * the table number and the page number (needed if a PageId is used as a 35 | * key in a hash table in the BufferPool, for example.) 36 | * @see BufferPool 37 | */ 38 | public int hashCode() { 39 | // some code goes here 40 | throw new UnsupportedOperationException("implement this"); 41 | } 42 | 43 | /** 44 | * Compares one PageId to another. 45 | * 46 | * @param o The object to compare against (must be a PageId) 47 | * @return true if the objects are equal (e.g., page numbers and table 48 | * ids are the same) 49 | */ 50 | public boolean equals(Object o) { 51 | // some code goes here 52 | return false; 53 | } 54 | 55 | /** 56 | * Return a representation of this object as an array of 57 | * integers, for writing to disk. Size of returned array must contain 58 | * number of integers that corresponds to number of args to one of the 59 | * constructors. 60 | */ 61 | public int[] serialize() { 62 | int data[] = new int[2]; 63 | 64 | data[0] = getTableId(); 65 | data[1] = getPageNumber(); 66 | 67 | return data; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/java/simpledb/IndexOpIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.util.*; 3 | 4 | /** IndexDBIterator is the interface that index access methods 5 | implement in SimpleDb. 6 | */ 7 | public interface IndexOpIterator extends OpIterator { 8 | /** Open the access method such that when getNext is called, it 9 | iterates through the tuples that satisfy ipred. 10 | @param ipred The predicate that is used to scan the index. 11 | */ 12 | public void open(IndexPredicate ipred) 13 | throws NoSuchElementException, DbException, TransactionAbortedException; 14 | 15 | /** Begin a new index scan with the specified predicate. 16 | @param ipred The predicate that is used to scan the index. 17 | */ 18 | public void rewind(IndexPredicate ipred) 19 | throws DbException, TransactionAbortedException; 20 | } 21 | -------------------------------------------------------------------------------- /src/java/simpledb/IndexPredicate.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * IndexPredicate compares a field which has index on it against a given value 7 | * @see IndexOpIterator 8 | */ 9 | public class IndexPredicate implements Serializable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | private Predicate.Op op; 14 | private Field fieldvalue; 15 | 16 | /** 17 | * Constructor. 18 | * 19 | * @param fvalue The value that the predicate compares against. 20 | * @param op The operation to apply (as defined in Predicate.Op); either 21 | * Predicate.Op.GREATER_THAN, Predicate.Op.LESS_THAN, Predicate.Op.EQUAL, 22 | * Predicate.Op.GREATER_THAN_OR_EQ, or Predicate.Op.LESS_THAN_OR_EQ 23 | * @see Predicate 24 | */ 25 | public IndexPredicate(Predicate.Op op, Field fvalue) { 26 | this.op = op; 27 | this.fieldvalue = fvalue; 28 | } 29 | 30 | public Field getField() { 31 | return fieldvalue; 32 | } 33 | 34 | public Predicate.Op getOp() { 35 | return op; 36 | } 37 | 38 | /** Return true if the fieldvalue in the supplied predicate 39 | is satisfied by this predicate's fieldvalue and 40 | operator. 41 | @param ipd The field to compare against. 42 | */ 43 | public boolean equals(IndexPredicate ipd) { 44 | if (ipd == null) 45 | return false; 46 | return (op.equals(ipd.op) && fieldvalue.equals(ipd.fieldvalue)); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/java/simpledb/Insert.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * Inserts tuples read from the child operator into the tableId specified in the 5 | * constructor 6 | */ 7 | public class Insert extends Operator { 8 | 9 | private static final long serialVersionUID = 1L; 10 | 11 | /** 12 | * Constructor. 13 | * 14 | * @param t 15 | * The transaction running the insert. 16 | * @param child 17 | * The child operator from which to read tuples to be inserted. 18 | * @param tableId 19 | * The table in which to insert tuples. 20 | * @throws DbException 21 | * if TupleDesc of child differs from table into which we are to 22 | * insert. 23 | */ 24 | public Insert(TransactionId t, OpIterator child, int tableId) 25 | throws DbException { 26 | // some code goes here 27 | } 28 | 29 | public TupleDesc getTupleDesc() { 30 | // some code goes here 31 | return null; 32 | } 33 | 34 | public void open() throws DbException, TransactionAbortedException { 35 | // some code goes here 36 | } 37 | 38 | public void close() { 39 | // some code goes here 40 | } 41 | 42 | public void rewind() throws DbException, TransactionAbortedException { 43 | // some code goes here 44 | } 45 | 46 | /** 47 | * Inserts tuples read from child into the tableId specified by the 48 | * constructor. It returns a one field tuple containing the number of 49 | * inserted records. Inserts should be passed through BufferPool. An 50 | * instances of BufferPool is available via Database.getBufferPool(). Note 51 | * that insert DOES NOT need check to see if a particular tuple is a 52 | * duplicate before inserting it. 53 | * 54 | * @return A 1-field tuple containing the number of inserted records, or 55 | * null if called more than once. 56 | * @see Database#getBufferPool 57 | * @see BufferPool#insertTuple 58 | */ 59 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 60 | // some code goes here 61 | return null; 62 | } 63 | 64 | @Override 65 | public OpIterator[] getChildren() { 66 | // some code goes here 67 | return null; 68 | } 69 | 70 | @Override 71 | public void setChildren(OpIterator[] children) { 72 | // some code goes here 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/java/simpledb/IntField.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Instance of Field that stores a single integer. 7 | */ 8 | public class IntField implements Field { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private final int value; 13 | 14 | public int getValue() { 15 | return value; 16 | } 17 | 18 | /** 19 | * Constructor. 20 | * 21 | * @param i The value of this field. 22 | */ 23 | public IntField(int i) { 24 | value = i; 25 | } 26 | 27 | public String toString() { 28 | return Integer.toString(value); 29 | } 30 | 31 | public int hashCode() { 32 | return value; 33 | } 34 | 35 | public boolean equals(Object field) { 36 | return ((IntField) field).value == value; 37 | } 38 | 39 | public void serialize(DataOutputStream dos) throws IOException { 40 | dos.writeInt(value); 41 | } 42 | 43 | /** 44 | * Compare the specified field to the value of this Field. 45 | * Return semantics are as specified by Field.compare 46 | * 47 | * @throws IllegalCastException if val is not an IntField 48 | * @see Field#compare 49 | */ 50 | public boolean compare(Predicate.Op op, Field val) { 51 | 52 | IntField iVal = (IntField) val; 53 | 54 | switch (op) { 55 | case EQUALS: 56 | return value == iVal.value; 57 | case NOT_EQUALS: 58 | return value != iVal.value; 59 | 60 | case GREATER_THAN: 61 | return value > iVal.value; 62 | 63 | case GREATER_THAN_OR_EQ: 64 | return value >= iVal.value; 65 | 66 | case LESS_THAN: 67 | return value < iVal.value; 68 | 69 | case LESS_THAN_OR_EQ: 70 | return value <= iVal.value; 71 | 72 | case LIKE: 73 | return value == iVal.value; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | /** 80 | * Return the Type of this field. 81 | * @return Type.INT_TYPE 82 | */ 83 | public Type getType() { 84 | return Type.INT_TYPE; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/java/simpledb/IntHistogram.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A class to represent a fixed-width histogram over a single integer-based field. 4 | */ 5 | public class IntHistogram { 6 | 7 | /** 8 | * Create a new IntHistogram. 9 | * 10 | * This IntHistogram should maintain a histogram of integer values that it receives. 11 | * It should split the histogram into "buckets" buckets. 12 | * 13 | * The values that are being histogrammed will be provided one-at-a-time through the "addValue()" function. 14 | * 15 | * Your implementation should use space and have execution time that are both 16 | * constant with respect to the number of values being histogrammed. For example, you shouldn't 17 | * simply store every value that you see in a sorted list. 18 | * 19 | * @param buckets The number of buckets to split the input value into. 20 | * @param min The minimum integer value that will ever be passed to this class for histogramming 21 | * @param max The maximum integer value that will ever be passed to this class for histogramming 22 | */ 23 | public IntHistogram(int buckets, int min, int max) { 24 | // some code goes here 25 | } 26 | 27 | /** 28 | * Add a value to the set of values that you are keeping a histogram of. 29 | * @param v Value to add to the histogram 30 | */ 31 | public void addValue(int v) { 32 | // some code goes here 33 | } 34 | 35 | /** 36 | * Estimate the selectivity of a particular predicate and operand on this table. 37 | * 38 | * For example, if "op" is "GREATER_THAN" and "v" is 5, 39 | * return your estimate of the fraction of elements that are greater than 5. 40 | * 41 | * @param op Operator 42 | * @param v Value 43 | * @return Predicted selectivity of this particular operator and value 44 | */ 45 | public double estimateSelectivity(Predicate.Op op, int v) { 46 | 47 | // some code goes here 48 | return -1.0; 49 | } 50 | 51 | /** 52 | * @return 53 | * the average selectivity of this histogram. 54 | * 55 | * This is not an indispensable method to implement the basic 56 | * join optimization. It may be needed if you want to 57 | * implement a more efficient optimization 58 | * */ 59 | public double avgSelectivity() 60 | { 61 | // some code goes here 62 | return 1.0; 63 | } 64 | 65 | /** 66 | * @return A string describing this histogram, for debugging purposes 67 | */ 68 | public String toString() { 69 | // some code goes here 70 | return null; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/java/simpledb/IntegerAggregator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * Knows how to compute some aggregate over a set of IntFields. 5 | */ 6 | public class IntegerAggregator implements Aggregator { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | /** 11 | * Aggregate constructor 12 | * 13 | * @param gbfield 14 | * the 0-based index of the group-by field in the tuple, or 15 | * NO_GROUPING if there is no grouping 16 | * @param gbfieldtype 17 | * the type of the group by field (e.g., Type.INT_TYPE), or null 18 | * if there is no grouping 19 | * @param afield 20 | * the 0-based index of the aggregate field in the tuple 21 | * @param what 22 | * the aggregation operator 23 | */ 24 | 25 | public IntegerAggregator(int gbfield, Type gbfieldtype, int afield, Op what) { 26 | // some code goes here 27 | } 28 | 29 | /** 30 | * Merge a new tuple into the aggregate, grouping as indicated in the 31 | * constructor 32 | * 33 | * @param tup 34 | * the Tuple containing an aggregate field and a group-by field 35 | */ 36 | public void mergeTupleIntoGroup(Tuple tup) { 37 | // some code goes here 38 | } 39 | 40 | /** 41 | * Create a OpIterator over group aggregate results. 42 | * 43 | * @return a OpIterator whose tuples are the pair (groupVal, aggregateVal) 44 | * if using group, or a single (aggregateVal) if no grouping. The 45 | * aggregateVal is determined by the type of aggregate specified in 46 | * the constructor. 47 | */ 48 | public OpIterator iterator() { 49 | // some code goes here 50 | throw new 51 | UnsupportedOperationException("please implement me for lab2"); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/java/simpledb/Join.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * The Join operator implements the relational join operation. 7 | */ 8 | public class Join extends Operator { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | /** 13 | * Constructor. Accepts two children to join and the predicate to join them 14 | * on 15 | * 16 | * @param p 17 | * The predicate to use to join the children 18 | * @param child1 19 | * Iterator for the left(outer) relation to join 20 | * @param child2 21 | * Iterator for the right(inner) relation to join 22 | */ 23 | public Join(JoinPredicate p, OpIterator child1, OpIterator child2) { 24 | // some code goes here 25 | } 26 | 27 | public JoinPredicate getJoinPredicate() { 28 | // some code goes here 29 | return null; 30 | } 31 | 32 | /** 33 | * @return 34 | * the field name of join field1. Should be quantified by 35 | * alias or table name. 36 | * */ 37 | public String getJoinField1Name() { 38 | // some code goes here 39 | return null; 40 | } 41 | 42 | /** 43 | * @return 44 | * the field name of join field2. Should be quantified by 45 | * alias or table name. 46 | * */ 47 | public String getJoinField2Name() { 48 | // some code goes here 49 | return null; 50 | } 51 | 52 | /** 53 | * @see simpledb.TupleDesc#merge(TupleDesc, TupleDesc) for possible 54 | * implementation logic. 55 | */ 56 | public TupleDesc getTupleDesc() { 57 | // some code goes here 58 | return null; 59 | } 60 | 61 | public void open() throws DbException, NoSuchElementException, 62 | TransactionAbortedException { 63 | // some code goes here 64 | } 65 | 66 | public void close() { 67 | // some code goes here 68 | } 69 | 70 | public void rewind() throws DbException, TransactionAbortedException { 71 | // some code goes here 72 | } 73 | 74 | /** 75 | * Returns the next tuple generated by the join, or null if there are no 76 | * more tuples. Logically, this is the next tuple in r1 cross r2 that 77 | * satisfies the join predicate. There are many possible implementations; 78 | * the simplest is a nested loops join. 79 | *

80 | * Note that the tuples returned from this particular implementation of Join 81 | * are simply the concatenation of joining tuples from the left and right 82 | * relation. Therefore, if an equality predicate is used there will be two 83 | * copies of the join attribute in the results. (Removing such duplicate 84 | * columns can be done with an additional projection operator if needed.) 85 | *

86 | * For example, if one tuple is {1,2,3} and the other tuple is {1,5,6}, 87 | * joined on equality of the first column, then this returns {1,2,3,1,5,6}. 88 | * 89 | * @return The next matching tuple. 90 | * @see JoinPredicate#filter 91 | */ 92 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 93 | // some code goes here 94 | return null; 95 | } 96 | 97 | @Override 98 | public OpIterator[] getChildren() { 99 | // some code goes here 100 | return null; 101 | } 102 | 103 | @Override 104 | public void setChildren(OpIterator[] children) { 105 | // some code goes here 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/java/simpledb/JoinPredicate.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * JoinPredicate compares fields of two tuples using a predicate. JoinPredicate 7 | * is most likely used by the Join operator. 8 | */ 9 | public class JoinPredicate implements Serializable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | /** 14 | * Constructor -- create a new predicate over two fields of two tuples. 15 | * 16 | * @param field1 17 | * The field index into the first tuple in the predicate 18 | * @param field2 19 | * The field index into the second tuple in the predicate 20 | * @param op 21 | * The operation to apply (as defined in Predicate.Op); either 22 | * Predicate.Op.GREATER_THAN, Predicate.Op.LESS_THAN, 23 | * Predicate.Op.EQUAL, Predicate.Op.GREATER_THAN_OR_EQ, or 24 | * Predicate.Op.LESS_THAN_OR_EQ 25 | * @see Predicate 26 | */ 27 | public JoinPredicate(int field1, Predicate.Op op, int field2) { 28 | // some code goes here 29 | } 30 | 31 | /** 32 | * Apply the predicate to the two specified tuples. The comparison can be 33 | * made through Field's compare method. 34 | * 35 | * @return true if the tuples satisfy the predicate. 36 | */ 37 | public boolean filter(Tuple t1, Tuple t2) { 38 | // some code goes here 39 | return false; 40 | } 41 | 42 | public int getField1() 43 | { 44 | // some code goes here 45 | return -1; 46 | } 47 | 48 | public int getField2() 49 | { 50 | // some code goes here 51 | return -1; 52 | } 53 | 54 | public Predicate.Op getOperator() 55 | { 56 | // some code goes here 57 | return null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/java/simpledb/LogicalFilterNode.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A LogicalFilterNode represents the parameters of a filter in the WHERE clause of a query. 4 |

5 | Filter is of the form t.f p c 6 |

7 | Where t is a table, f is a field in t, p is a predicate, and c is a constant 8 | */ 9 | public class LogicalFilterNode { 10 | /** The alias of a table (or the name if no alias) over which the filter ranges */ 11 | public String tableAlias; 12 | 13 | /** The predicate in the filter */ 14 | public Predicate.Op p; 15 | 16 | /* The constant on the right side of the filter */ 17 | public String c; 18 | 19 | /** The field from t which is in the filter. The pure name, without alias or tablename*/ 20 | public String fieldPureName; 21 | 22 | public String fieldQuantifiedName; 23 | 24 | public LogicalFilterNode(String table, String field, Predicate.Op pred, String constant) { 25 | tableAlias = table; 26 | p = pred; 27 | c = constant; 28 | String[] tmps = field.split("[.]"); 29 | if (tmps.length>1) 30 | fieldPureName = tmps[tmps.length-1]; 31 | else 32 | fieldPureName=field; 33 | this.fieldQuantifiedName = tableAlias+"."+fieldPureName; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/java/simpledb/LogicalJoinNode.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A LogicalJoinNode represens the state needed of a join of two 4 | * tables in a LogicalQueryPlan */ 5 | public class LogicalJoinNode { 6 | 7 | /** The first table to join (may be null). It's the alias of the table (if no alias, the true table name) */ 8 | public String t1Alias; 9 | 10 | /** The second table to join (may be null). It's the alias of the table, (if no alias, the true table name).*/ 11 | public String t2Alias; 12 | 13 | /** The name of the field in t1 to join with. It's the pure name of a field, rather that alias.field. */ 14 | public String f1PureName; 15 | 16 | public String f1QuantifiedName; 17 | 18 | /** The name of the field in t2 to join with. It's the pure name of a field.*/ 19 | public String f2PureName; 20 | 21 | public String f2QuantifiedName; 22 | 23 | /** The join predicate */ 24 | public Predicate.Op p; 25 | 26 | public LogicalJoinNode() { 27 | } 28 | 29 | public LogicalJoinNode(String table1, String table2, String joinField1, String joinField2, Predicate.Op pred) { 30 | t1Alias = table1; 31 | t2Alias = table2; 32 | String[] tmps = joinField1.split("[.]"); 33 | if (tmps.length>1) 34 | f1PureName = tmps[tmps.length-1]; 35 | else 36 | f1PureName=joinField1; 37 | tmps = joinField2.split("[.]"); 38 | if (tmps.length>1) 39 | f2PureName = tmps[tmps.length-1]; 40 | else 41 | f2PureName = joinField2; 42 | p = pred; 43 | this.f1QuantifiedName = t1Alias+"."+this.f1PureName; 44 | this.f2QuantifiedName = t2Alias+"."+this.f2PureName; 45 | } 46 | 47 | /** Return a new LogicalJoinNode with the inner and outer (t1.f1 48 | * and t2.f2) tables swapped. */ 49 | public LogicalJoinNode swapInnerOuter() { 50 | Predicate.Op newp; 51 | if (p == Predicate.Op.GREATER_THAN) 52 | newp = Predicate.Op.LESS_THAN; 53 | else if (p == Predicate.Op.GREATER_THAN_OR_EQ) 54 | newp = Predicate.Op.LESS_THAN_OR_EQ; 55 | else if (p == Predicate.Op.LESS_THAN) 56 | newp = Predicate.Op.GREATER_THAN; 57 | else if (p == Predicate.Op.LESS_THAN_OR_EQ) 58 | newp = Predicate.Op.GREATER_THAN_OR_EQ; 59 | else 60 | newp = p; 61 | 62 | LogicalJoinNode j2 = new LogicalJoinNode(t2Alias,t1Alias,f2PureName,f1PureName, newp); 63 | return j2; 64 | } 65 | 66 | @Override public boolean equals(Object o) { 67 | LogicalJoinNode j2 =(LogicalJoinNode)o; 68 | return (j2.t1Alias.equals(t1Alias) || j2.t1Alias.equals(t2Alias)) && (j2.t2Alias.equals(t1Alias) || j2.t2Alias.equals(t2Alias)); 69 | } 70 | 71 | @Override public String toString() { 72 | return t1Alias + ":" + t2Alias ;//+ ";" + f1 + " " + p + " " + f2; 73 | } 74 | 75 | @Override public int hashCode() { 76 | return t1Alias.hashCode() + t2Alias.hashCode() + f1PureName.hashCode() + f2PureName.hashCode(); 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/java/simpledb/LogicalScanNode.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A LogicalScanNode represents table in the FROM list in a 4 | * LogicalQueryPlan */ 5 | public class LogicalScanNode { 6 | 7 | /** The name (alias) of the table as it is used in the query */ 8 | public String alias; 9 | 10 | /** The table identifier (can be passed to {@link Catalog#getDatabaseFile}) 11 | * to retrieve a DbFile */ 12 | public int t; 13 | 14 | public LogicalScanNode(int table, String tableAlias) { 15 | this.alias = tableAlias; 16 | this.t = table; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/java/simpledb/LogicalSelectListNode.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A LogicalSelectListNode represents a clause in the select list in 4 | * a LogicalQueryPlan 5 | */ 6 | public class LogicalSelectListNode { 7 | /** The field name being selected; the name may be (optionally) be 8 | * qualified with a table name or alias. 9 | */ 10 | public String fname; 11 | 12 | /** The aggregation operation over the field (if any) */ 13 | public String aggOp; 14 | 15 | public LogicalSelectListNode(String aggOp, String fname) { 16 | this.aggOp = aggOp; 17 | this.fname = fname; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/java/simpledb/LogicalSubplanJoinNode.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** A LogicalSubplanJoinNode represens the state needed of a join of a 4 | * table to a subplan in a LogicalQueryPlan -- inherits state from 5 | * {@link LogicalJoinNode}; t2 and f2 should always be null 6 | */ 7 | public class LogicalSubplanJoinNode extends LogicalJoinNode { 8 | 9 | /** The subplan (used on the inner) of the join */ 10 | OpIterator subPlan; 11 | 12 | public LogicalSubplanJoinNode(String table1, String joinField1, OpIterator sp, Predicate.Op pred) { 13 | t1Alias = table1; 14 | String[] tmps = joinField1.split("[.]"); 15 | if (tmps.length>1) 16 | f1PureName = tmps[tmps.length-1]; 17 | else 18 | f1PureName=joinField1; 19 | f1QuantifiedName=t1Alias+"."+f1PureName; 20 | subPlan = sp; 21 | p = pred; 22 | } 23 | 24 | @Override public int hashCode() { 25 | return t1Alias.hashCode() + f1PureName.hashCode() + subPlan.hashCode(); 26 | } 27 | 28 | @Override public boolean equals(Object o) { 29 | LogicalJoinNode j2 =(LogicalJoinNode)o; 30 | if (!(o instanceof LogicalSubplanJoinNode)) 31 | return false; 32 | 33 | return (j2.t1Alias.equals(t1Alias) && j2.f1PureName.equals(f1PureName) && ((LogicalSubplanJoinNode)o).subPlan.equals(subPlan)); 34 | } 35 | 36 | public LogicalSubplanJoinNode swapInnerOuter() { 37 | LogicalSubplanJoinNode j2 = new LogicalSubplanJoinNode(t1Alias,f1PureName,subPlan, p); 38 | return j2; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/java/simpledb/OpIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.io.Serializable; 3 | import java.util.*; 4 | 5 | /** 6 | * OpIterator is the iterator interface that all SimpleDB operators should 7 | * implement. If the iterator is not open, none of the methods should work, 8 | * and should throw an IllegalStateException. In addition to any 9 | * resource allocation/deallocation, an open method should call any 10 | * child iterator open methods, and in a close method, an iterator 11 | * should call its children's close methods. 12 | */ 13 | public interface OpIterator extends Serializable{ 14 | /** 15 | * Opens the iterator. This must be called before any of the other methods. 16 | * @throws DbException when there are problems opening/accessing the database. 17 | */ 18 | public void open() 19 | throws DbException, TransactionAbortedException; 20 | 21 | /** Returns true if the iterator has more tuples. 22 | * @return true f the iterator has more tuples. 23 | * @throws IllegalStateException If the iterator has not been opened 24 | */ 25 | public boolean hasNext() throws DbException, TransactionAbortedException; 26 | 27 | /** 28 | * Returns the next tuple from the operator (typically implementing by reading 29 | * from a child operator or an access method). 30 | * 31 | * @return the next tuple in the iteration. 32 | * @throws NoSuchElementException if there are no more tuples. 33 | * @throws IllegalStateException If the iterator has not been opened 34 | */ 35 | public Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException; 36 | 37 | /** 38 | * Resets the iterator to the start. 39 | * @throws DbException when rewind is unsupported. 40 | * @throws IllegalStateException If the iterator has not been opened 41 | */ 42 | public void rewind() throws DbException, TransactionAbortedException; 43 | 44 | /** 45 | * Returns the TupleDesc associated with this OpIterator. 46 | * @return the TupleDesc associated with this OpIterator. 47 | */ 48 | public TupleDesc getTupleDesc(); 49 | 50 | /** 51 | * Closes the iterator. When the iterator is closed, calling next(), 52 | * hasNext(), or rewind() should fail by throwing IllegalStateException. 53 | */ 54 | public void close(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/java/simpledb/Operator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.NoSuchElementException; 4 | 5 | /** 6 | * Abstract class for implementing operators. It handles close, 7 | * next and hasNext. Subclasses only need to implement 8 | * open and readNext. 9 | */ 10 | public abstract class Operator implements OpIterator { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | public boolean hasNext() throws DbException, TransactionAbortedException { 15 | if (!this.open) 16 | throw new IllegalStateException("Operator not yet open"); 17 | 18 | if (next == null) 19 | next = fetchNext(); 20 | return next != null; 21 | } 22 | 23 | public Tuple next() throws DbException, TransactionAbortedException, 24 | NoSuchElementException { 25 | if (next == null) { 26 | next = fetchNext(); 27 | if (next == null) 28 | throw new NoSuchElementException(); 29 | } 30 | 31 | Tuple result = next; 32 | next = null; 33 | return result; 34 | } 35 | 36 | /** 37 | * Returns the next Tuple in the iterator, or null if the iteration is 38 | * finished. Operator uses this method to implement both next 39 | * and hasNext. 40 | * 41 | * @return the next Tuple in the iterator, or null if the iteration is 42 | * finished. 43 | */ 44 | protected abstract Tuple fetchNext() throws DbException, 45 | TransactionAbortedException; 46 | 47 | /** 48 | * Closes this iterator. If overridden by a subclass, they should call 49 | * super.close() in order for Operator's internal state to be consistent. 50 | */ 51 | public void close() { 52 | // Ensures that a future call to next() will fail 53 | next = null; 54 | this.open = false; 55 | } 56 | 57 | private Tuple next = null; 58 | private boolean open = false; 59 | private int estimatedCardinality = 0; 60 | 61 | public void open() throws DbException, TransactionAbortedException { 62 | this.open = true; 63 | } 64 | 65 | /** 66 | * @return return the children DbIterators of this operator. If there is 67 | * only one child, return an array of only one element. For join 68 | * operators, the order of the children is not important. But they 69 | * should be consistent among multiple calls. 70 | * */ 71 | public abstract OpIterator[] getChildren(); 72 | 73 | /** 74 | * Set the children(child) of this operator. If the operator has only one 75 | * child, children[0] should be used. If the operator is a join, children[0] 76 | * and children[1] should be used. 77 | * 78 | * 79 | * @param children 80 | * the DbIterators which are to be set as the children(child) of 81 | * this operator 82 | * */ 83 | public abstract void setChildren(OpIterator[] children); 84 | 85 | /** 86 | * @return return the TupleDesc of the output tuples of this operator 87 | * */ 88 | public abstract TupleDesc getTupleDesc(); 89 | 90 | /** 91 | * @return The estimated cardinality of this operator. Will only be used in 92 | * lab7 93 | * */ 94 | public int getEstimatedCardinality() { 95 | return this.estimatedCardinality; 96 | } 97 | 98 | /** 99 | * @param card 100 | * The estimated cardinality of this operator Will only be used 101 | * in lab7 102 | * */ 103 | protected void setEstimatedCardinality(int card) { 104 | this.estimatedCardinality = card; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/java/simpledb/OrderBy.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * OrderBy is an operator that implements a relational ORDER BY. 7 | */ 8 | public class OrderBy extends Operator { 9 | 10 | private static final long serialVersionUID = 1L; 11 | private OpIterator child; 12 | private TupleDesc td; 13 | private ArrayList childTups = new ArrayList(); 14 | private int orderByField; 15 | private String orderByFieldName; 16 | private Iterator it; 17 | private boolean asc; 18 | 19 | /** 20 | * Creates a new OrderBy node over the tuples from the iterator. 21 | * 22 | * @param orderbyField 23 | * the field to which the sort is applied. 24 | * @param asc 25 | * true if the sort order is ascending. 26 | * @param child 27 | * the tuples to sort. 28 | */ 29 | public OrderBy(int orderbyField, boolean asc, OpIterator child) { 30 | this.child = child; 31 | td = child.getTupleDesc(); 32 | this.orderByField = orderbyField; 33 | this.orderByFieldName = td.getFieldName(orderbyField); 34 | this.asc = asc; 35 | } 36 | 37 | public boolean isASC() 38 | { 39 | return this.asc; 40 | } 41 | 42 | public int getOrderByField() 43 | { 44 | return this.orderByField; 45 | } 46 | 47 | public String getOrderFieldName() 48 | { 49 | return this.orderByFieldName; 50 | } 51 | 52 | public TupleDesc getTupleDesc() { 53 | return td; 54 | } 55 | 56 | public void open() throws DbException, NoSuchElementException, 57 | TransactionAbortedException { 58 | child.open(); 59 | // load all the tuples in a collection, and sort it 60 | while (child.hasNext()) 61 | childTups.add((Tuple) child.next()); 62 | Collections.sort(childTups, new TupleComparator(orderByField, asc)); 63 | it = childTups.iterator(); 64 | super.open(); 65 | } 66 | 67 | public void close() { 68 | super.close(); 69 | it = null; 70 | } 71 | 72 | public void rewind() throws DbException, TransactionAbortedException { 73 | it = childTups.iterator(); 74 | } 75 | 76 | /** 77 | * Operator.fetchNext implementation. Returns tuples from the child operator 78 | * in order 79 | * 80 | * @return The next tuple in the ordering, or null if there are no more 81 | * tuples 82 | */ 83 | protected Tuple fetchNext() throws NoSuchElementException, 84 | TransactionAbortedException, DbException { 85 | if (it != null && it.hasNext()) { 86 | return it.next(); 87 | } else 88 | return null; 89 | } 90 | 91 | @Override 92 | public OpIterator[] getChildren() { 93 | return new OpIterator[] { this.child }; 94 | } 95 | 96 | @Override 97 | public void setChildren(OpIterator[] children) { 98 | this.child = children[0]; 99 | } 100 | 101 | } 102 | 103 | class TupleComparator implements Comparator { 104 | int field; 105 | boolean asc; 106 | 107 | public TupleComparator(int field, boolean asc) { 108 | this.field = field; 109 | this.asc = asc; 110 | } 111 | 112 | public int compare(Tuple o1, Tuple o2) { 113 | Field t1 = (o1).getField(field); 114 | Field t2 = (o2).getField(field); 115 | if (t1.compare(Predicate.Op.EQUALS, t2)) 116 | return 0; 117 | if (t1.compare(Predicate.Op.GREATER_THAN, t2)) 118 | return asc ? 1 : -1; 119 | else 120 | return asc ? -1 : 1; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/java/simpledb/Page.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * Page is the interface used to represent pages that are resident in the 5 | * BufferPool. Typically, DbFiles will read and write pages from disk. 6 | *

7 | * Pages may be "dirty", indicating that they have been modified since they 8 | * were last written out to disk. 9 | * 10 | * For recovery purposes, pages MUST have a single constructor of the form: 11 | * Page(PageId id, byte[] data) 12 | */ 13 | public interface Page { 14 | 15 | /** 16 | * Return the id of this page. The id is a unique identifier for a page 17 | * that can be used to look up the page on disk or determine if the page 18 | * is resident in the buffer pool. 19 | * 20 | * @return the id of this page 21 | */ 22 | public PageId getId(); 23 | 24 | /** 25 | * Get the id of the transaction that last dirtied this page, or null if the page is clean.. 26 | * 27 | * @return The id of the transaction that last dirtied this page, or null 28 | */ 29 | public TransactionId isDirty(); 30 | 31 | /** 32 | * Set the dirty state of this page as dirtied by a particular transaction 33 | */ 34 | public void markDirty(boolean dirty, TransactionId tid); 35 | 36 | /** 37 | * Generates a byte array representing the contents of this page. 38 | * Used to serialize this page to disk. 39 | *

40 | * The invariant here is that it should be possible to pass the byte array 41 | * generated by getPageData to the Page constructor and have it produce 42 | * an identical Page object. 43 | * 44 | * @return A byte array correspond to the bytes of this page. 45 | */ 46 | 47 | public byte[] getPageData(); 48 | 49 | /** Provide a representation of this page before any modifications were made 50 | to it. Used by recovery. 51 | */ 52 | public Page getBeforeImage(); 53 | 54 | /* 55 | * a transaction that wrote this page just committed it. 56 | * copy current content to the before image. 57 | */ 58 | public void setBeforeImage(); 59 | } 60 | -------------------------------------------------------------------------------- /src/java/simpledb/PageId.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** PageId is an interface to a specific page of a specific table. */ 4 | public interface PageId { 5 | 6 | /** Return a representation of this page id object as a collection of 7 | integers (used for logging) 8 | 9 | This class MUST have a constructor that accepts n integer parameters, 10 | where n is the number of integers returned in the array from serialize. 11 | */ 12 | public int[] serialize(); 13 | 14 | /** @return the unique tableid hashcode with this PageId */ 15 | public int getTableId(); 16 | 17 | /** 18 | * @return a hash code for this page, represented by the concatenation of 19 | * the table number and the page number (needed if a PageId is used as a 20 | * key in a hash table in the BufferPool, for example.) 21 | * @see BufferPool 22 | */ 23 | public int hashCode(); 24 | 25 | /** 26 | * Compares one PageId to another. 27 | * 28 | * @param o The object to compare against (must be a PageId) 29 | * @return true if the objects are equal (e.g., page numbers and table 30 | * ids are the same) 31 | */ 32 | public boolean equals(Object o); 33 | 34 | public int getPageNumber(); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/java/simpledb/ParsingException.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.lang.Exception; 3 | 4 | public class ParsingException extends Exception { 5 | public ParsingException(String string) { 6 | super(string); 7 | } 8 | 9 | public ParsingException(Exception e) { 10 | super(e); 11 | } 12 | 13 | /** 14 | * 15 | */ 16 | private static final long serialVersionUID = 1L; 17 | } 18 | -------------------------------------------------------------------------------- /src/java/simpledb/Permissions.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * Class representing requested permissions to a relation/file. 5 | * Private constructor with two static objects READ_ONLY and READ_WRITE that 6 | * represent the two levels of permission. 7 | */ 8 | public class Permissions { 9 | int permLevel; 10 | 11 | private Permissions(int permLevel) { 12 | this.permLevel = permLevel; 13 | } 14 | 15 | public String toString() { 16 | if (permLevel == 0) 17 | return "READ_ONLY"; 18 | if (permLevel == 1) 19 | return "READ_WRITE"; 20 | return "UNKNOWN"; 21 | } 22 | 23 | public static final Permissions READ_ONLY = new Permissions(0); 24 | public static final Permissions READ_WRITE = new Permissions(1); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/java/simpledb/PlanCache.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.util.HashMap; 3 | import java.util.Set; 4 | import java.util.Vector; 5 | 6 | /** A PlanCache is a helper class that can be used to store the best 7 | * way to order a given set of joins */ 8 | public class PlanCache { 9 | HashMap,Vector> bestOrders= new HashMap,Vector>(); 10 | HashMap,Double> bestCosts= new HashMap,Double>(); 11 | HashMap,Integer> bestCardinalities = new HashMap,Integer>(); 12 | 13 | /** Add a new cost, cardinality and ordering for a particular join set. Does not verify that the 14 | new cost is less than any previously added cost -- simply adds or replaces an existing plan for the 15 | specified join set 16 | @param s the set of joins for which a new ordering (plan) is being added 17 | @param cost the estimated cost of the specified plan 18 | @param card the estimatied cardinality of the specified plan 19 | @param order the ordering of the joins in the plan 20 | */ 21 | void addPlan(Set s, double cost, int card, Vector order) { 22 | bestOrders.put(s,order); 23 | bestCosts.put(s,cost); 24 | bestCardinalities.put(s,card); 25 | } 26 | 27 | /** Find the best join order in the cache for the specified plan 28 | @param s the set of joins to look up the best order for 29 | @return the best order for s in the cache 30 | */ 31 | Vector getOrder(Set s) { 32 | return bestOrders.get(s); 33 | } 34 | 35 | /** Find the cost of the best join order in the cache for the specified plan 36 | @param s the set of joins to look up the best cost for 37 | @return the cost of the best order for s in the cache 38 | */ 39 | double getCost(Set s) { 40 | return bestCosts.get(s); 41 | } 42 | 43 | /** Find the cardinality of the best join order in the cache for the specified plan 44 | @param s the set of joins to look up the best cardinality for 45 | @return the cardinality of the best order for s in the cache 46 | */ 47 | int getCard(Set s) { 48 | return bestCardinalities.get(s); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/java/simpledb/Predicate.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Predicate compares tuples to a specified Field value. 7 | */ 8 | public class Predicate implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | /** Constants used for return codes in Field.compare */ 13 | public enum Op implements Serializable { 14 | EQUALS, GREATER_THAN, LESS_THAN, LESS_THAN_OR_EQ, GREATER_THAN_OR_EQ, LIKE, NOT_EQUALS; 15 | 16 | /** 17 | * Interface to access operations by integer value for command-line 18 | * convenience. 19 | * 20 | * @param i 21 | * a valid integer Op index 22 | */ 23 | public static Op getOp(int i) { 24 | return values()[i]; 25 | } 26 | 27 | public String toString() { 28 | if (this == EQUALS) 29 | return "="; 30 | if (this == GREATER_THAN) 31 | return ">"; 32 | if (this == LESS_THAN) 33 | return "<"; 34 | if (this == LESS_THAN_OR_EQ) 35 | return "<="; 36 | if (this == GREATER_THAN_OR_EQ) 37 | return ">="; 38 | if (this == LIKE) 39 | return "LIKE"; 40 | if (this == NOT_EQUALS) 41 | return "<>"; 42 | throw new IllegalStateException("impossible to reach here"); 43 | } 44 | 45 | } 46 | 47 | /** 48 | * Constructor. 49 | * 50 | * @param field 51 | * field number of passed in tuples to compare against. 52 | * @param op 53 | * operation to use for comparison 54 | * @param operand 55 | * field value to compare passed in tuples to 56 | */ 57 | public Predicate(int field, Op op, Field operand) { 58 | // some code goes here 59 | } 60 | 61 | /** 62 | * @return the field number 63 | */ 64 | public int getField() 65 | { 66 | // some code goes here 67 | return -1; 68 | } 69 | 70 | /** 71 | * @return the operator 72 | */ 73 | public Op getOp() 74 | { 75 | // some code goes here 76 | return null; 77 | } 78 | 79 | /** 80 | * @return the operand 81 | */ 82 | public Field getOperand() 83 | { 84 | // some code goes here 85 | return null; 86 | } 87 | 88 | /** 89 | * Compares the field number of t specified in the constructor to the 90 | * operand field specified in the constructor using the operator specific in 91 | * the constructor. The comparison can be made through Field's compare 92 | * method. 93 | * 94 | * @param t 95 | * The tuple to compare against 96 | * @return true if the comparison is true, false otherwise. 97 | */ 98 | public boolean filter(Tuple t) { 99 | // some code goes here 100 | return false; 101 | } 102 | 103 | /** 104 | * Returns something useful, like "f = field_id op = op_string operand = 105 | * operand_string" 106 | */ 107 | public String toString() { 108 | // some code goes here 109 | return ""; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/java/simpledb/Project.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Project is an operator that implements a relational projection. 7 | */ 8 | public class Project extends Operator { 9 | 10 | private static final long serialVersionUID = 1L; 11 | private OpIterator child; 12 | private TupleDesc td; 13 | private ArrayList outFieldIds; 14 | 15 | /** 16 | * Constructor accepts a child operator to read tuples to apply projection 17 | * to and a list of fields in output tuple 18 | * 19 | * @param fieldList 20 | * The ids of the fields child's tupleDesc to project out 21 | * @param typesList 22 | * the types of the fields in the final projection 23 | * @param child 24 | * The child operator 25 | */ 26 | public Project(ArrayList fieldList, ArrayList typesList, 27 | OpIterator child) { 28 | this(fieldList,typesList.toArray(new Type[]{}),child); 29 | } 30 | 31 | public Project(ArrayList fieldList, Type[] types, 32 | OpIterator child) { 33 | this.child = child; 34 | outFieldIds = fieldList; 35 | String[] fieldAr = new String[fieldList.size()]; 36 | TupleDesc childtd = child.getTupleDesc(); 37 | 38 | for (int i = 0; i < fieldAr.length; i++) { 39 | fieldAr[i] = childtd.getFieldName(fieldList.get(i)); 40 | } 41 | td = new TupleDesc(types, fieldAr); 42 | } 43 | 44 | public TupleDesc getTupleDesc() { 45 | return td; 46 | } 47 | 48 | public void open() throws DbException, NoSuchElementException, 49 | TransactionAbortedException { 50 | child.open(); 51 | super.open(); 52 | } 53 | 54 | public void close() { 55 | super.close(); 56 | child.close(); 57 | } 58 | 59 | public void rewind() throws DbException, TransactionAbortedException { 60 | child.rewind(); 61 | } 62 | 63 | /** 64 | * Operator.fetchNext implementation. Iterates over tuples from the child 65 | * operator, projecting out the fields from the tuple 66 | * 67 | * @return The next tuple, or null if there are no more tuples 68 | */ 69 | protected Tuple fetchNext() throws NoSuchElementException, 70 | TransactionAbortedException, DbException { 71 | while (child.hasNext()) { 72 | Tuple t = child.next(); 73 | Tuple newTuple = new Tuple(td); 74 | newTuple.setRecordId(t.getRecordId()); 75 | for (int i = 0; i < td.numFields(); i++) { 76 | newTuple.setField(i, t.getField(outFieldIds.get(i))); 77 | } 78 | return newTuple; 79 | } 80 | return null; 81 | } 82 | 83 | @Override 84 | public OpIterator[] getChildren() { 85 | return new OpIterator[] { this.child }; 86 | } 87 | 88 | @Override 89 | public void setChildren(OpIterator[] children) { 90 | if (this.child!=children[0]) 91 | { 92 | this.child = children[0]; 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /src/java/simpledb/Query.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | 6 | /** 7 | * Query is a wrapper class to manage the execution of queries. It takes a query 8 | * plan in the form of a high level OpIterator (built by initiating the 9 | * constructors of query plans) and runs it as a part of a specified 10 | * transaction. 11 | * 12 | * @author Sam Madden 13 | */ 14 | 15 | public class Query implements Serializable { 16 | 17 | private static final long serialVersionUID = 1L; 18 | 19 | transient private OpIterator op; 20 | transient private LogicalPlan logicalPlan; 21 | TransactionId tid; 22 | transient private boolean started = false; 23 | 24 | public TransactionId getTransactionId() { 25 | return this.tid; 26 | } 27 | 28 | public void setLogicalPlan(LogicalPlan lp) { 29 | this.logicalPlan = lp; 30 | } 31 | 32 | public LogicalPlan getLogicalPlan() { 33 | return this.logicalPlan; 34 | } 35 | 36 | public void setPhysicalPlan(OpIterator pp) { 37 | this.op = pp; 38 | } 39 | 40 | public OpIterator getPhysicalPlan() { 41 | return this.op; 42 | } 43 | 44 | public Query(TransactionId t) { 45 | tid = t; 46 | } 47 | 48 | public Query(OpIterator root, TransactionId t) { 49 | op = root; 50 | tid = t; 51 | } 52 | 53 | public void start() throws IOException, DbException, 54 | TransactionAbortedException { 55 | op.open(); 56 | 57 | started = true; 58 | } 59 | 60 | public TupleDesc getOutputTupleDesc() { 61 | return this.op.getTupleDesc(); 62 | } 63 | 64 | /** @return true if there are more tuples remaining. */ 65 | public boolean hasNext() throws DbException, TransactionAbortedException { 66 | return op.hasNext(); 67 | } 68 | 69 | /** 70 | * Returns the next tuple, or throws NoSuchElementException if the iterator 71 | * is closed. 72 | * 73 | * @return The next tuple in the iterator 74 | * @throws DbException 75 | * If there is an error in the database system 76 | * @throws NoSuchElementException 77 | * If the iterator has finished iterating 78 | * @throws TransactionAbortedException 79 | * If the transaction is aborted (e.g., due to a deadlock) 80 | */ 81 | public Tuple next() throws DbException, NoSuchElementException, 82 | TransactionAbortedException { 83 | if (!started) 84 | throw new DbException("Database not started."); 85 | 86 | return op.next(); 87 | } 88 | 89 | /** Close the iterator */ 90 | public void close() throws IOException { 91 | op.close(); 92 | started = false; 93 | } 94 | 95 | public void execute() throws IOException, DbException, TransactionAbortedException { 96 | TupleDesc td = this.getOutputTupleDesc(); 97 | 98 | String names = ""; 99 | for (int i = 0; i < td.numFields(); i++) { 100 | names += td.getFieldName(i) + "\t"; 101 | } 102 | System.out.println(names); 103 | for (int i = 0; i < names.length() + td.numFields() * 4; i++) { 104 | System.out.print("-"); 105 | } 106 | System.out.println(""); 107 | 108 | this.start(); 109 | int cnt = 0; 110 | while (this.hasNext()) { 111 | Tuple tup = this.next(); 112 | System.out.println(tup); 113 | cnt++; 114 | } 115 | System.out.println("\n " + cnt + " rows."); 116 | this.close(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/java/simpledb/RecordId.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * A RecordId is a reference to a specific tuple on a specific page of a 7 | * specific table. 8 | */ 9 | public class RecordId implements Serializable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | /** 14 | * Creates a new RecordId referring to the specified PageId and tuple 15 | * number. 16 | * 17 | * @param pid 18 | * the pageid of the page on which the tuple resides 19 | * @param tupleno 20 | * the tuple number within the page. 21 | */ 22 | public RecordId(PageId pid, int tupleno) { 23 | // some code goes here 24 | } 25 | 26 | /** 27 | * @return the tuple number this RecordId references. 28 | */ 29 | public int getTupleNumber() { 30 | // some code goes here 31 | return 0; 32 | } 33 | 34 | /** 35 | * @return the page id this RecordId references. 36 | */ 37 | public PageId getPageId() { 38 | // some code goes here 39 | return null; 40 | } 41 | 42 | /** 43 | * Two RecordId objects are considered equal if they represent the same 44 | * tuple. 45 | * 46 | * @return True if this and o represent the same tuple 47 | */ 48 | @Override 49 | public boolean equals(Object o) { 50 | // some code goes here 51 | throw new UnsupportedOperationException("implement this"); 52 | } 53 | 54 | /** 55 | * You should implement the hashCode() so that two equal RecordId instances 56 | * (with respect to equals()) have the same hashCode(). 57 | * 58 | * @return An int that is the same for equal RecordId objects. 59 | */ 60 | @Override 61 | public int hashCode() { 62 | // some code goes here 63 | throw new UnsupportedOperationException("implement this"); 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/java/simpledb/SeqScan.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * SeqScan is an implementation of a sequential scan access method that reads 7 | * each tuple of a table in no particular order (e.g., as they are laid out on 8 | * disk). 9 | */ 10 | public class SeqScan implements OpIterator { 11 | 12 | private static final long serialVersionUID = 1L; 13 | 14 | /** 15 | * Creates a sequential scan over the specified table as a part of the 16 | * specified transaction. 17 | * 18 | * @param tid 19 | * The transaction this scan is running as a part of. 20 | * @param tableid 21 | * the table to scan. 22 | * @param tableAlias 23 | * the alias of this table (needed by the parser); the returned 24 | * tupleDesc should have fields with name tableAlias.fieldName 25 | * (note: this class is not responsible for handling a case where 26 | * tableAlias or fieldName are null. It shouldn't crash if they 27 | * are, but the resulting name can be null.fieldName, 28 | * tableAlias.null, or null.null). 29 | */ 30 | public SeqScan(TransactionId tid, int tableid, String tableAlias) { 31 | // some code goes here 32 | } 33 | 34 | /** 35 | * @return 36 | * return the table name of the table the operator scans. This should 37 | * be the actual name of the table in the catalog of the database 38 | * */ 39 | public String getTableName() { 40 | return null; 41 | } 42 | 43 | /** 44 | * @return Return the alias of the table this operator scans. 45 | * */ 46 | public String getAlias() 47 | { 48 | // some code goes here 49 | return null; 50 | } 51 | 52 | /** 53 | * Reset the tableid, and tableAlias of this operator. 54 | * @param tableid 55 | * the table to scan. 56 | * @param tableAlias 57 | * the alias of this table (needed by the parser); the returned 58 | * tupleDesc should have fields with name tableAlias.fieldName 59 | * (note: this class is not responsible for handling a case where 60 | * tableAlias or fieldName are null. It shouldn't crash if they 61 | * are, but the resulting name can be null.fieldName, 62 | * tableAlias.null, or null.null). 63 | */ 64 | public void reset(int tableid, String tableAlias) { 65 | // some code goes here 66 | } 67 | 68 | public SeqScan(TransactionId tid, int tableId) { 69 | this(tid, tableId, Database.getCatalog().getTableName(tableId)); 70 | } 71 | 72 | public void open() throws DbException, TransactionAbortedException { 73 | // some code goes here 74 | } 75 | 76 | /** 77 | * Returns the TupleDesc with field names from the underlying HeapFile, 78 | * prefixed with the tableAlias string from the constructor. This prefix 79 | * becomes useful when joining tables containing a field(s) with the same 80 | * name. The alias and name should be separated with a "." character 81 | * (e.g., "alias.fieldName"). 82 | * 83 | * @return the TupleDesc with field names from the underlying HeapFile, 84 | * prefixed with the tableAlias string from the constructor. 85 | */ 86 | public TupleDesc getTupleDesc() { 87 | // some code goes here 88 | return null; 89 | } 90 | 91 | public boolean hasNext() throws TransactionAbortedException, DbException { 92 | // some code goes here 93 | return false; 94 | } 95 | 96 | public Tuple next() throws NoSuchElementException, 97 | TransactionAbortedException, DbException { 98 | // some code goes here 99 | return null; 100 | } 101 | 102 | public void close() { 103 | // some code goes here 104 | } 105 | 106 | public void rewind() throws DbException, NoSuchElementException, 107 | TransactionAbortedException { 108 | // some code goes here 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/java/simpledb/SimpleDb.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | import java.io.*; 3 | 4 | public class SimpleDb { 5 | public static void main (String args[]) 6 | throws DbException, TransactionAbortedException, IOException { 7 | // convert a file 8 | if(args[0].equals("convert")) { 9 | try { 10 | if (args.length<3 || args.length>5){ 11 | System.err.println("Unexpected number of arguments to convert "); 12 | return; 13 | } 14 | File sourceTxtFile=new File(args[1]); 15 | File targetDatFile=new File(args[1].replaceAll(".txt", ".dat")); 16 | int numOfAttributes=Integer.parseInt(args[2]); 17 | Type[] ts = new Type[numOfAttributes]; 18 | char fieldSeparator=','; 19 | 20 | if (args.length == 3) 21 | for (int i=0;i c = Class.forName("simpledb.Parser"); 80 | Class s = String[].class; 81 | 82 | java.lang.reflect.Method m = c.getMethod("main", s); 83 | m.invoke(null, (java.lang.Object)newargs); 84 | } catch (ClassNotFoundException cne) { 85 | System.out.println("Class Parser not found -- perhaps you are trying to run the parser as a part of lab1?"); 86 | } 87 | catch (Exception e) { 88 | System.out.println("Error in parser."); 89 | e.printStackTrace(); 90 | } 91 | 92 | } 93 | else { 94 | System.err.println("Unknown command: " + args[0]); 95 | System.exit(1); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/java/simpledb/StringAggregator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * Knows how to compute some aggregate over a set of StringFields. 5 | */ 6 | public class StringAggregator implements Aggregator { 7 | 8 | private static final long serialVersionUID = 1L; 9 | 10 | /** 11 | * Aggregate constructor 12 | * @param gbfield the 0-based index of the group-by field in the tuple, or NO_GROUPING if there is no grouping 13 | * @param gbfieldtype the type of the group by field (e.g., Type.INT_TYPE), or null if there is no grouping 14 | * @param afield the 0-based index of the aggregate field in the tuple 15 | * @param what aggregation operator to use -- only supports COUNT 16 | * @throws IllegalArgumentException if what != COUNT 17 | */ 18 | 19 | public StringAggregator(int gbfield, Type gbfieldtype, int afield, Op what) { 20 | // some code goes here 21 | } 22 | 23 | /** 24 | * Merge a new tuple into the aggregate, grouping as indicated in the constructor 25 | * @param tup the Tuple containing an aggregate field and a group-by field 26 | */ 27 | public void mergeTupleIntoGroup(Tuple tup) { 28 | // some code goes here 29 | } 30 | 31 | /** 32 | * Create a OpIterator over group aggregate results. 33 | * 34 | * @return a OpIterator whose tuples are the pair (groupVal, 35 | * aggregateVal) if using group, or a single (aggregateVal) if no 36 | * grouping. The aggregateVal is determined by the type of 37 | * aggregate specified in the constructor. 38 | */ 39 | public OpIterator iterator() { 40 | // some code goes here 41 | throw new UnsupportedOperationException("please implement me for lab2"); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/java/simpledb/StringField.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Instance of Field that stores a single String of a fixed length. 7 | */ 8 | public class StringField implements Field { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | private final String value; 13 | private final int maxSize; 14 | 15 | public String getValue() { 16 | return value; 17 | } 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param s 23 | * The value of this field. 24 | * @param maxSize 25 | * The maximum size of this string 26 | */ 27 | public StringField(String s, int maxSize) { 28 | this.maxSize = maxSize; 29 | 30 | if (s.length() > maxSize) 31 | value = s.substring(0, maxSize); 32 | else 33 | value = s; 34 | } 35 | 36 | public String toString() { 37 | return value; 38 | } 39 | 40 | public int hashCode() { 41 | return value.hashCode(); 42 | } 43 | 44 | public boolean equals(Object field) { 45 | return ((StringField) field).value.equals(value); 46 | } 47 | 48 | /** 49 | * Write this string to dos. Always writes maxSize + 4 bytes to the passed 50 | * in dos. First four bytes are string length, next bytes are string, with 51 | * remainder padded with 0 to maxSize. 52 | * 53 | * @param dos 54 | * Where the string is written 55 | */ 56 | public void serialize(DataOutputStream dos) throws IOException { 57 | String s = value; 58 | int overflow = maxSize - s.length(); 59 | if (overflow < 0) { 60 | String news = s.substring(0, maxSize); 61 | s = news; 62 | } 63 | dos.writeInt(s.length()); 64 | dos.writeBytes(s); 65 | while (overflow-- > 0) 66 | dos.write((byte) 0); 67 | } 68 | 69 | /** 70 | * Compare the specified field to the value of this Field. Return semantics 71 | * are as specified by Field.compare 72 | * 73 | * @throws IllegalCastException 74 | * if val is not a StringField 75 | * @see Field#compare 76 | */ 77 | public boolean compare(Predicate.Op op, Field val) { 78 | 79 | StringField iVal = (StringField) val; 80 | int cmpVal = value.compareTo(iVal.value); 81 | 82 | switch (op) { 83 | case EQUALS: 84 | return cmpVal == 0; 85 | 86 | case NOT_EQUALS: 87 | return cmpVal != 0; 88 | 89 | case GREATER_THAN: 90 | return cmpVal > 0; 91 | 92 | case GREATER_THAN_OR_EQ: 93 | return cmpVal >= 0; 94 | 95 | case LESS_THAN: 96 | return cmpVal < 0; 97 | 98 | case LESS_THAN_OR_EQ: 99 | return cmpVal <= 0; 100 | 101 | case LIKE: 102 | return value.indexOf(iVal.value) >= 0; 103 | } 104 | 105 | return false; 106 | } 107 | 108 | /** 109 | * @return the Type for this Field 110 | */ 111 | public Type getType() { 112 | 113 | return Type.STRING_TYPE; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/java/simpledb/StringHistogram.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | /** 4 | * A class to represent a fixed-width histogram over a single String-based 5 | * field. 6 | */ 7 | public class StringHistogram { 8 | final IntHistogram hist; 9 | 10 | /** 11 | * Create a new StringHistogram with a specified number of buckets. 12 | *

13 | * Our implementation is written in terms of an IntHistogram by converting 14 | * each String to an integer. 15 | * 16 | * @param buckets 17 | * the number of buckets 18 | */ 19 | public StringHistogram(int buckets) { 20 | hist = new IntHistogram(buckets, minVal(), maxVal()); 21 | } 22 | 23 | /** 24 | * Convert a string to an integer, with the property that if the return 25 | * value(s1) < return value(s2), then s1 < s2 26 | */ 27 | private int stringToInt(String s) { 28 | int i; 29 | int v = 0; 30 | for (i = 3; i >= 0; i--) { 31 | if (s.length() > 3 - i) { 32 | int ci = (int) s.charAt(3 - i); 33 | v += (ci) << (i * 8); 34 | } 35 | } 36 | 37 | // XXX: hack to avoid getting wrong results for 38 | // strings which don't output in the range min to max 39 | if (!(s.equals("") || s.equals("zzzz"))) { 40 | if (v < minVal()) { 41 | v = minVal(); 42 | } 43 | 44 | if (v > maxVal()) { 45 | v = maxVal(); 46 | } 47 | } 48 | 49 | return v; 50 | } 51 | 52 | /** @return the maximum value indexed by the histogram */ 53 | int maxVal() { 54 | return stringToInt("zzzz"); 55 | } 56 | 57 | /** @return the minimum value indexed by the histogram */ 58 | int minVal() { 59 | return stringToInt(""); 60 | } 61 | 62 | /** Add a new value to thte histogram */ 63 | public void addValue(String s) { 64 | int val = stringToInt(s); 65 | hist.addValue(val); 66 | } 67 | 68 | /** 69 | * Estimate the selectivity (as a double between 0 and 1) of the specified 70 | * predicate over the specified string 71 | * 72 | * @param op 73 | * The operation being applied 74 | * @param s 75 | * The string to apply op to 76 | */ 77 | public double estimateSelectivity(Predicate.Op op, String s) { 78 | int val = stringToInt(s); 79 | return hist.estimateSelectivity(op, val); 80 | } 81 | 82 | /** 83 | * @return the average selectivity of this histogram. 84 | * 85 | * This is not an indispensable method to implement the basic join 86 | * optimization. It may be needed if you want to implement a more 87 | * efficient optimization 88 | * */ 89 | public double avgSelectivity() { 90 | return hist.avgSelectivity(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/java/simpledb/Transaction.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Transaction encapsulates information about the state of 7 | * a transaction and manages transaction commit / abort. 8 | */ 9 | 10 | public class Transaction { 11 | private final TransactionId tid; 12 | volatile boolean started = false; 13 | 14 | public Transaction() { 15 | tid = new TransactionId(); 16 | } 17 | 18 | /** Start the transaction running */ 19 | public void start() { 20 | started = true; 21 | try { 22 | Database.getLogFile().logXactionBegin(tid); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | public TransactionId getId() { 29 | return tid; 30 | } 31 | 32 | /** Finish the transaction */ 33 | public void commit() throws IOException { 34 | transactionComplete(false); 35 | } 36 | 37 | /** Finish the transaction */ 38 | public void abort() throws IOException { 39 | transactionComplete(true); 40 | } 41 | 42 | /** Handle the details of transaction commit / abort */ 43 | public void transactionComplete(boolean abort) throws IOException { 44 | 45 | if (started) { 46 | //write commit / abort records 47 | if (abort) { 48 | Database.getLogFile().logAbort(tid); //does rollback too 49 | } else { 50 | //write all the dirty pages for this transaction out 51 | Database.getBufferPool().flushPages(tid); 52 | Database.getLogFile().logCommit(tid); 53 | } 54 | 55 | try { 56 | Database.getBufferPool().transactionComplete(tid, !abort); // release locks 57 | } catch (IOException e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | //setting this here means we could possibly write multiple abort records -- OK? 62 | started = false; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/java/simpledb/TransactionAbortedException.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.lang.Exception; 4 | 5 | /** Exception that is thrown when a transaction has aborted. */ 6 | public class TransactionAbortedException extends Exception { 7 | private static final long serialVersionUID = 1L; 8 | 9 | public TransactionAbortedException() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/java/simpledb/TransactionId.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | import java.util.concurrent.atomic.AtomicLong; 5 | 6 | /** 7 | * TransactionId is a class that contains the identifier of a transaction. 8 | */ 9 | public class TransactionId implements Serializable { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | static AtomicLong counter = new AtomicLong(0); 14 | final long myid; 15 | 16 | public TransactionId() { 17 | myid = counter.getAndIncrement(); 18 | } 19 | 20 | public long getId() { 21 | return myid; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object obj) { 26 | if (this == obj) 27 | return true; 28 | if (obj == null) 29 | return false; 30 | if (getClass() != obj.getClass()) 31 | return false; 32 | TransactionId other = (TransactionId) obj; 33 | if (myid != other.myid) 34 | return false; 35 | return true; 36 | } 37 | 38 | @Override 39 | public int hashCode() { 40 | final int prime = 31; 41 | int result = 1; 42 | result = prime * result + (int) (myid ^ (myid >>> 32)); 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/java/simpledb/Tuple.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.io.Serializable; 4 | import java.util.Arrays; 5 | import java.util.Iterator; 6 | 7 | /** 8 | * Tuple maintains information about the contents of a tuple. Tuples have a 9 | * specified schema specified by a TupleDesc object and contain Field objects 10 | * with the data for each field. 11 | */ 12 | public class Tuple implements Serializable { 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | /** 17 | * Create a new tuple with the specified schema (type). 18 | * 19 | * @param td 20 | * the schema of this tuple. It must be a valid TupleDesc 21 | * instance with at least one field. 22 | */ 23 | public Tuple(TupleDesc td) { 24 | // some code goes here 25 | } 26 | 27 | /** 28 | * @return The TupleDesc representing the schema of this tuple. 29 | */ 30 | public TupleDesc getTupleDesc() { 31 | // some code goes here 32 | return null; 33 | } 34 | 35 | /** 36 | * @return The RecordId representing the location of this tuple on disk. May 37 | * be null. 38 | */ 39 | public RecordId getRecordId() { 40 | // some code goes here 41 | return null; 42 | } 43 | 44 | /** 45 | * Set the RecordId information for this tuple. 46 | * 47 | * @param rid 48 | * the new RecordId for this tuple. 49 | */ 50 | public void setRecordId(RecordId rid) { 51 | // some code goes here 52 | } 53 | 54 | /** 55 | * Change the value of the ith field of this tuple. 56 | * 57 | * @param i 58 | * index of the field to change. It must be a valid index. 59 | * @param f 60 | * new value for the field. 61 | */ 62 | public void setField(int i, Field f) { 63 | // some code goes here 64 | } 65 | 66 | /** 67 | * @return the value of the ith field, or null if it has not been set. 68 | * 69 | * @param i 70 | * field index to return. Must be a valid index. 71 | */ 72 | public Field getField(int i) { 73 | // some code goes here 74 | return null; 75 | } 76 | 77 | /** 78 | * Returns the contents of this Tuple as a string. Note that to pass the 79 | * system tests, the format needs to be as follows: 80 | * 81 | * column1\tcolumn2\tcolumn3\t...\tcolumnN 82 | * 83 | * where \t is any whitespace (except a newline) 84 | */ 85 | public String toString() { 86 | // some code goes here 87 | throw new UnsupportedOperationException("Implement this"); 88 | } 89 | 90 | /** 91 | * @return 92 | * An iterator which iterates over all the fields of this tuple 93 | * */ 94 | public Iterator fields() 95 | { 96 | // some code goes here 97 | return null; 98 | } 99 | 100 | /** 101 | * reset the TupleDesc of this tuple (only affecting the TupleDesc) 102 | * */ 103 | public void resetTupleDesc(TupleDesc td) 104 | { 105 | // some code goes here 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/java/simpledb/TupleIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Implements a OpIterator by wrapping an Iterable. 7 | */ 8 | public class TupleIterator implements OpIterator { 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = 1L; 13 | Iterator i = null; 14 | TupleDesc td = null; 15 | Iterable tuples = null; 16 | 17 | /** 18 | * Constructs an iterator from the specified Iterable, and the specified 19 | * descriptor. 20 | * 21 | * @param tuples 22 | * The set of tuples to iterate over 23 | */ 24 | public TupleIterator(TupleDesc td, Iterable tuples) { 25 | this.td = td; 26 | this.tuples = tuples; 27 | 28 | // check that all tuples are the right TupleDesc 29 | for (Tuple t : tuples) { 30 | if (!t.getTupleDesc().equals(td)) 31 | throw new IllegalArgumentException( 32 | "incompatible tuple in tuple set"); 33 | } 34 | } 35 | 36 | public void open() { 37 | i = tuples.iterator(); 38 | } 39 | 40 | public boolean hasNext() { 41 | return i.hasNext(); 42 | } 43 | 44 | public Tuple next() { 45 | return i.next(); 46 | } 47 | 48 | public void rewind() { 49 | close(); 50 | open(); 51 | } 52 | 53 | public TupleDesc getTupleDesc() { 54 | return td; 55 | } 56 | 57 | public void close() { 58 | i = null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/java/simpledb/Type.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.text.ParseException; 4 | import java.io.*; 5 | 6 | /** 7 | * Class representing a type in SimpleDB. 8 | * Types are static objects defined by this class; hence, the Type 9 | * constructor is private. 10 | */ 11 | public enum Type implements Serializable { 12 | INT_TYPE() { 13 | @Override 14 | public int getLen() { 15 | return 4; 16 | } 17 | 18 | @Override 19 | public Field parse(DataInputStream dis) throws ParseException { 20 | try { 21 | return new IntField(dis.readInt()); 22 | } catch (IOException e) { 23 | throw new ParseException("couldn't parse", 0); 24 | } 25 | } 26 | 27 | }, STRING_TYPE() { 28 | @Override 29 | public int getLen() { 30 | return STRING_LEN+4; 31 | } 32 | 33 | @Override 34 | public Field parse(DataInputStream dis) throws ParseException { 35 | try { 36 | int strLen = dis.readInt(); 37 | byte bs[] = new byte[strLen]; 38 | dis.read(bs); 39 | dis.skipBytes(STRING_LEN-strLen); 40 | return new StringField(new String(bs), STRING_LEN); 41 | } catch (IOException e) { 42 | throw new ParseException("couldn't parse", 0); 43 | } 44 | } 45 | }; 46 | 47 | public static final int STRING_LEN = 128; 48 | 49 | /** 50 | * @return the number of bytes required to store a field of this type. 51 | */ 52 | public abstract int getLen(); 53 | 54 | /** 55 | * @return a Field object of the same type as this object that has contents 56 | * read from the specified DataInputStream. 57 | * @param dis The input stream to read from 58 | * @throws ParseException if the data read from the input stream is not 59 | * of the appropriate type. 60 | */ 61 | public abstract Field parse(DataInputStream dis) throws ParseException; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /test/simpledb/BTreeDeadlockTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.Predicate.Op; 4 | import simpledb.BTreeUtility.*; 5 | import simpledb.systemtest.SimpleDbTestBase; 6 | 7 | import java.util.*; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | import junit.framework.JUnit4TestAdapter; 11 | 12 | public class BTreeDeadlockTest extends SimpleDbTestBase { 13 | private Random rand; 14 | 15 | private static final int POLL_INTERVAL = 100; 16 | private static final int WAIT_INTERVAL = 200; 17 | 18 | // just so we have a pointer shorter than Database.getBufferPool 19 | private BufferPool bp; 20 | private BTreeFile bf; 21 | private int item1; 22 | private int item2; 23 | private int count1; 24 | private int count2; 25 | 26 | /** 27 | * Set up initial resources for each unit test. 28 | */ 29 | @Before public void setUp() throws Exception { 30 | // create a packed B+ tree with no empty slots 31 | bf = BTreeUtility.createRandomBTreeFile(2, 253008, null, null, 0); 32 | rand = new Random(); 33 | item1 = rand.nextInt(BTreeUtility.MAX_RAND_VALUE); 34 | item2 = rand.nextInt(BTreeUtility.MAX_RAND_VALUE); 35 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 36 | 37 | // first make sure that item1 is not contained in our B+ tree 38 | TransactionId tid = new TransactionId(); 39 | DbFileIterator it = bf.indexIterator(tid, new IndexPredicate(Op.EQUALS, new IntField(item1))); 40 | it.open(); 41 | ArrayList tuples = new ArrayList(); 42 | while(it.hasNext()) { 43 | tuples.add(it.next()); 44 | } 45 | for(Tuple t : tuples) { 46 | bp.deleteTuple(tid, t); 47 | } 48 | 49 | // this is the number of tuples we must insert to replace the deleted tuples 50 | // and cause the root node to split 51 | count1 = tuples.size() + 1; 52 | 53 | // do the same thing for item 2 54 | it = bf.indexIterator(tid, new IndexPredicate(Op.EQUALS, new IntField(item2))); 55 | it.open(); 56 | tuples.clear(); 57 | while(it.hasNext()) { 58 | tuples.add(it.next()); 59 | } 60 | for(Tuple t : tuples) { 61 | bp.deleteTuple(tid, t); 62 | } 63 | 64 | // this is the number of tuples we must insert to replace the deleted tuples 65 | // and cause the root node to split 66 | count2 = tuples.size() + 1; 67 | 68 | // clear all state from the buffer pool, increase the number of pages 69 | bp.flushAllPages(); 70 | bp = Database.resetBufferPool(500); 71 | 72 | } 73 | 74 | /** 75 | * Helper method to clean up the syntax of starting a BTreeWriter thread. 76 | * The parameters pass through to the BTreeWriter constructor. 77 | */ 78 | public BTreeUtility.BTreeWriter startWriter(TransactionId tid, 79 | int item, int count) { 80 | 81 | BTreeWriter bw = new BTreeWriter(tid, bf, item, count); 82 | bw.start(); 83 | return bw; 84 | } 85 | 86 | /** 87 | * Not-so-unit test to construct a deadlock situation. 88 | * 89 | * This test causes two different transactions to update two (probably) different leaf nodes 90 | * Each transaction can happily insert tuples until the page fills up, but then 91 | * it needs to obtain a write lock on the root node in order to split the page. This will cause 92 | * a deadlock situation. 93 | */ 94 | @Test public void testReadWriteDeadlock() throws Exception { 95 | System.out.println("testReadWriteDeadlock constructing deadlock:"); 96 | 97 | TransactionId tid1 = new TransactionId(); 98 | TransactionId tid2 = new TransactionId(); 99 | 100 | Database.getBufferPool().getPage(tid1, BTreeRootPtrPage.getId(bf.getId()), Permissions.READ_ONLY); 101 | Database.getBufferPool().getPage(tid2, BTreeRootPtrPage.getId(bf.getId()), Permissions.READ_ONLY); 102 | 103 | // allow read locks to acquire 104 | Thread.sleep(POLL_INTERVAL); 105 | 106 | BTreeWriter writer1 = startWriter(tid1, item1, count1); 107 | BTreeWriter writer2 = startWriter(tid2, item2, count2); 108 | 109 | while (true) { 110 | Thread.sleep(POLL_INTERVAL); 111 | 112 | if(writer1.succeeded() || writer2.succeeded()) break; 113 | 114 | if (writer1.getError() != null) { 115 | writer1 = null; 116 | bp.transactionComplete(tid1); 117 | Thread.sleep(rand.nextInt(WAIT_INTERVAL)); 118 | 119 | tid1 = new TransactionId(); 120 | writer1 = startWriter(tid1, item1, count1); 121 | } 122 | 123 | if (writer2.getError() != null) { 124 | writer2 = null; 125 | bp.transactionComplete(tid2); 126 | Thread.sleep(rand.nextInt(WAIT_INTERVAL)); 127 | 128 | tid2 = new TransactionId(); 129 | writer2 = startWriter(tid2, item2, count2); 130 | } 131 | 132 | } 133 | 134 | System.out.println("testReadWriteDeadlock resolved deadlock"); 135 | } 136 | 137 | /** 138 | * JUnit suite target 139 | */ 140 | public static junit.framework.Test suite() { 141 | return new JUnit4TestAdapter(BTreeDeadlockTest.class); 142 | } 143 | 144 | } 145 | 146 | -------------------------------------------------------------------------------- /test/simpledb/BTreePageIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import junit.framework.JUnit4TestAdapter; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import simpledb.systemtest.SimpleDbTestBase; 12 | 13 | public class BTreePageIdTest extends SimpleDbTestBase { 14 | 15 | private BTreePageId rootPtrId; 16 | private BTreePageId internalId; 17 | private BTreePageId leafId; 18 | private BTreePageId headerId; 19 | 20 | @Before public void createPid() { 21 | rootPtrId = new BTreePageId(1, 0, BTreePageId.ROOT_PTR); 22 | internalId = new BTreePageId(1, 1, BTreePageId.INTERNAL); 23 | leafId = new BTreePageId(1, 2, BTreePageId.LEAF); 24 | headerId = new BTreePageId(1, 3, BTreePageId.HEADER); 25 | } 26 | 27 | /** 28 | * Unit test for BTreePageId.getTableId() 29 | */ 30 | @Test public void getTableId() { 31 | assertEquals(1, rootPtrId.getTableId()); 32 | assertEquals(1, internalId.getTableId()); 33 | assertEquals(1, leafId.getTableId()); 34 | assertEquals(1, headerId.getTableId()); 35 | } 36 | 37 | /** 38 | * Unit test for BTreePageId.pageno() 39 | */ 40 | @Test public void pageno() { 41 | assertEquals(0, rootPtrId.getPageNumber()); 42 | assertEquals(1, internalId.getPageNumber()); 43 | assertEquals(2, leafId.getPageNumber()); 44 | assertEquals(3, headerId.getPageNumber()); 45 | } 46 | 47 | /** 48 | * Unit test for BTreePageId.hashCode() 49 | */ 50 | @Test public void testHashCode() { 51 | int code1, code2, code3, code4; 52 | 53 | // NOTE(ghuo): the hashCode could be anything. test determinism, 54 | // at least. 55 | code1 = rootPtrId.hashCode(); 56 | assertEquals(code1, rootPtrId.hashCode()); 57 | assertEquals(code1, rootPtrId.hashCode()); 58 | 59 | code2 = internalId.hashCode(); 60 | assertEquals(code2, internalId.hashCode()); 61 | assertEquals(code2, internalId.hashCode()); 62 | 63 | code3 = leafId.hashCode(); 64 | assertEquals(code3, leafId.hashCode()); 65 | assertEquals(code3, leafId.hashCode()); 66 | 67 | code4 = headerId.hashCode(); 68 | assertEquals(code4, headerId.hashCode()); 69 | assertEquals(code4, headerId.hashCode()); 70 | } 71 | 72 | /** 73 | * Unit test for BTreePageId.equals() 74 | */ 75 | @Test public void equals() { 76 | BTreePageId pid1 = new BTreePageId(1, 1, BTreePageId.LEAF); 77 | BTreePageId pid1Copy = new BTreePageId(1, 1, BTreePageId.LEAF); 78 | BTreePageId pid2 = new BTreePageId(2, 2, BTreePageId.LEAF); 79 | BTreePageId pid3 = new BTreePageId(1, 1, BTreePageId.INTERNAL); 80 | 81 | // .equals() with null should return false 82 | assertFalse(pid1.equals(null)); 83 | 84 | // .equals() with the wrong type should return false 85 | assertFalse(pid1.equals(new Object())); 86 | 87 | assertTrue(pid1.equals(pid1)); 88 | assertTrue(pid1.equals(pid1Copy)); 89 | assertTrue(pid1Copy.equals(pid1)); 90 | assertTrue(pid2.equals(pid2)); 91 | assertTrue(pid3.equals(pid3)); 92 | 93 | assertFalse(pid1.equals(pid2)); 94 | assertFalse(pid1Copy.equals(pid2)); 95 | assertFalse(pid2.equals(pid1)); 96 | assertFalse(pid2.equals(pid1Copy)); 97 | assertFalse(pid1.equals(pid3)); 98 | assertFalse(pid3.equals(pid1)); 99 | } 100 | 101 | /** 102 | * JUnit suite target 103 | */ 104 | public static junit.framework.Test suite() { 105 | return new JUnit4TestAdapter(BTreePageIdTest.class); 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /test/simpledb/BTreeRootPtrPageTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.TestUtil.SkeletonFile; 4 | import simpledb.systemtest.SimpleDbTestBase; 5 | import simpledb.systemtest.SystemTestUtil; 6 | 7 | //import java.io.File; 8 | import java.io.IOException; 9 | 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | import junit.framework.JUnit4TestAdapter; 15 | 16 | public class BTreeRootPtrPageTest extends SimpleDbTestBase { 17 | private BTreePageId pid; 18 | 19 | public static final byte[] EXAMPLE_DATA; 20 | static { 21 | // Identify the root page and page category 22 | int root = 1; 23 | int rootCategory = BTreePageId.LEAF; 24 | int header = 2; 25 | 26 | // Convert it to a BTreeRootPtrPage 27 | try { 28 | EXAMPLE_DATA = BTreeFileEncoder.convertToRootPtrPage(root, rootCategory, header); 29 | } catch (IOException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | 34 | /** 35 | * Set up initial resources for each unit test. 36 | */ 37 | @Before public void addTable() throws Exception { 38 | this.pid = new BTreePageId(-1, 0, BTreePageId.ROOT_PTR); 39 | Database.getCatalog().addTable(new SkeletonFile(-1, Utility.getTupleDesc(2)), SystemTestUtil.getUUID()); 40 | } 41 | 42 | /** 43 | * Unit test for BTreeRootPtrPage.getId() 44 | */ 45 | @Test public void getId() throws Exception { 46 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 47 | assertEquals(pid, page.getId()); 48 | } 49 | 50 | /** 51 | * Unit test for BTreeRootPtrPage.getRootId() 52 | */ 53 | @Test public void getRootId() throws Exception { 54 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 55 | assertEquals(new BTreePageId(pid.getTableId(), 1, BTreePageId.LEAF), page.getRootId()); 56 | } 57 | 58 | /** 59 | * Unit test for BTreeRootPtrPage.setRootId() 60 | */ 61 | @Test public void setRootId() throws Exception { 62 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 63 | BTreePageId id = new BTreePageId(pid.getTableId(), 1, BTreePageId.INTERNAL); 64 | page.setRootId(id); 65 | assertEquals(id, page.getRootId()); 66 | 67 | id = new BTreePageId(pid.getTableId(), 1, BTreePageId.ROOT_PTR); 68 | try { 69 | page.setRootId(id); 70 | throw new Exception("should not be able to set rootId to RootPtr node; expected DbException"); 71 | } catch (DbException e) { 72 | // explicitly ignored 73 | } 74 | 75 | id = new BTreePageId(pid.getTableId() + 1, 1, BTreePageId.INTERNAL); 76 | try { 77 | page.setRootId(id); 78 | throw new Exception("should not be able to set rootId to a page from a different table; expected DbException"); 79 | } catch (DbException e) { 80 | // explicitly ignored 81 | } 82 | } 83 | 84 | /** 85 | * Unit test for BTreeRootPtrPage.getHeaderId() 86 | */ 87 | @Test public void getHeaderId() throws Exception { 88 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 89 | assertEquals(new BTreePageId(pid.getTableId(), 2, BTreePageId.HEADER), page.getHeaderId()); 90 | } 91 | 92 | /** 93 | * Unit test for BTreeRootPtrPage.setHeaderId() 94 | */ 95 | @Test public void setHeaderId() throws Exception { 96 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 97 | BTreePageId id = new BTreePageId(pid.getTableId(), 3, BTreePageId.HEADER); 98 | page.setHeaderId(id); 99 | assertEquals(id, page.getHeaderId()); 100 | 101 | id = new BTreePageId(pid.getTableId(), 2, BTreePageId.ROOT_PTR); 102 | try { 103 | page.setHeaderId(id); 104 | throw new Exception("should not be able to set headerId to RootPtr node; expected DbException"); 105 | } catch (DbException e) { 106 | // explicitly ignored 107 | } 108 | 109 | id = new BTreePageId(pid.getTableId() + 1, 1, BTreePageId.HEADER); 110 | try { 111 | page.setHeaderId(id); 112 | throw new Exception("should not be able to set rootId to a page from a different table; expected DbException"); 113 | } catch (DbException e) { 114 | // explicitly ignored 115 | } 116 | } 117 | 118 | /** 119 | * Unit test for BTreeRootPtrPage.isDirty() 120 | */ 121 | @Test public void testDirty() throws Exception { 122 | TransactionId tid = new TransactionId(); 123 | BTreeRootPtrPage page = new BTreeRootPtrPage(pid, EXAMPLE_DATA); 124 | page.markDirty(true, tid); 125 | TransactionId dirtier = page.isDirty(); 126 | assertEquals(true, dirtier != null); 127 | assertEquals(true, dirtier == tid); 128 | 129 | page.markDirty(false, tid); 130 | dirtier = page.isDirty(); 131 | assertEquals(false, dirtier != null); 132 | } 133 | 134 | /** 135 | * JUnit suite target 136 | */ 137 | public static junit.framework.Test suite() { 138 | return new JUnit4TestAdapter(BTreeRootPtrPageTest.class); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /test/simpledb/CatalogTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.NoSuchElementException; 6 | import java.util.Random; 7 | 8 | import junit.framework.Assert; 9 | import junit.framework.JUnit4TestAdapter; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import simpledb.TestUtil.SkeletonFile; 15 | import simpledb.systemtest.SimpleDbTestBase; 16 | import simpledb.systemtest.SystemTestUtil; 17 | 18 | public class CatalogTest extends SimpleDbTestBase { 19 | private static Random r = new Random(); 20 | private static String name = SystemTestUtil.getUUID(); 21 | private static int id1 = r.nextInt(); 22 | private static int id2 = r.nextInt(); 23 | private String nameThisTestRun; 24 | 25 | @Before public void addTables() throws Exception { 26 | Database.getCatalog().clear(); 27 | nameThisTestRun = SystemTestUtil.getUUID(); 28 | Database.getCatalog().addTable(new SkeletonFile(id1, Utility.getTupleDesc(2)), nameThisTestRun); 29 | Database.getCatalog().addTable(new SkeletonFile(id2, Utility.getTupleDesc(2)), name); 30 | } 31 | 32 | /** 33 | * Unit test for Catalog.getTupleDesc() 34 | */ 35 | @Test public void getTupleDesc() throws Exception { 36 | TupleDesc expected = Utility.getTupleDesc(2); 37 | TupleDesc actual = Database.getCatalog().getTupleDesc(id1); 38 | 39 | assertEquals(expected, actual); 40 | } 41 | 42 | /** 43 | * Unit test for Catalog.getTableId() 44 | */ 45 | @Test public void getTableId() { 46 | assertEquals(id2, Database.getCatalog().getTableId(name)); 47 | assertEquals(id1, Database.getCatalog().getTableId(nameThisTestRun)); 48 | 49 | try { 50 | Database.getCatalog().getTableId(null); 51 | Assert.fail("Should not find table with null name"); 52 | } catch (NoSuchElementException e) { 53 | // Expected to get here 54 | } 55 | 56 | try { 57 | Database.getCatalog().getTableId("foo"); 58 | Assert.fail("Should not find table with name foo"); 59 | } catch (NoSuchElementException e) { 60 | // Expected to get here 61 | } 62 | } 63 | 64 | /** 65 | * Unit test for Catalog.getDatabaseFile() 66 | */ 67 | 68 | @Test public void getDatabaseFile() throws Exception { 69 | DbFile f = Database.getCatalog().getDatabaseFile(id1); 70 | 71 | // NOTE(ghuo): we try not to dig too deeply into the DbFile API here; we 72 | // rely on HeapFileTest for that. perform some basic checks. 73 | assertEquals(id1, f.getId()); 74 | } 75 | 76 | /** 77 | * Check that duplicate names are handled correctly 78 | */ 79 | @Test public void handleDuplicateNames() throws Exception { 80 | int id3 = r.nextInt(); 81 | Database.getCatalog().addTable(new SkeletonFile(id3, Utility.getTupleDesc(2)), name); 82 | assertEquals(id3, Database.getCatalog().getTableId(name)); 83 | } 84 | 85 | /** 86 | * Check that duplicate file ids are handled correctly 87 | */ 88 | @Test public void handleDuplicateIds() throws Exception { 89 | String newName = SystemTestUtil.getUUID(); 90 | DbFile f = new SkeletonFile(id2, Utility.getTupleDesc(2)); 91 | Database.getCatalog().addTable(f, newName); 92 | assertEquals(newName, Database.getCatalog().getTableName(id2)); 93 | assertEquals(f, Database.getCatalog().getDatabaseFile(id2)); 94 | } 95 | 96 | /** 97 | * JUnit suite target 98 | */ 99 | public static junit.framework.Test suite() { 100 | return new JUnit4TestAdapter(CatalogTest.class); 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /test/simpledb/FilterTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertTrue; 6 | import junit.framework.JUnit4TestAdapter; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import simpledb.systemtest.SimpleDbTestBase; 12 | 13 | public class FilterTest extends SimpleDbTestBase { 14 | 15 | int testWidth = 3; 16 | OpIterator scan; 17 | 18 | /** 19 | * Initialize each unit test 20 | */ 21 | @Before public void setUp() { 22 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 23 | } 24 | 25 | /** 26 | * Unit test for Filter.getTupleDesc() 27 | */ 28 | @Test public void getTupleDesc() { 29 | Predicate pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 30 | Filter op = new Filter(pred, scan); 31 | TupleDesc expected = Utility.getTupleDesc(testWidth); 32 | TupleDesc actual = op.getTupleDesc(); 33 | assertEquals(expected, actual); 34 | } 35 | 36 | /** 37 | * Unit test for Filter.rewind() 38 | */ 39 | @Test public void rewind() throws Exception { 40 | Predicate pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 41 | Filter op = new Filter(pred, scan); 42 | op.open(); 43 | assertTrue(op.hasNext()); 44 | assertNotNull(op.next()); 45 | assertTrue(TestUtil.checkExhausted(op)); 46 | 47 | op.rewind(); 48 | Tuple expected = Utility.getHeapTuple(0, testWidth); 49 | Tuple actual = op.next(); 50 | assertTrue(TestUtil.compareTuples(expected, actual)); 51 | op.close(); 52 | } 53 | 54 | /** 55 | * Unit test for Filter.getNext() using a < predicate that filters 56 | * some tuples 57 | */ 58 | @Test public void filterSomeLessThan() throws Exception { 59 | Predicate pred; 60 | pred = new Predicate(0, Predicate.Op.LESS_THAN, TestUtil.getField(2)); 61 | Filter op = new Filter(pred, scan); 62 | TestUtil.MockScan expectedOut = new TestUtil.MockScan(-5, 2, testWidth); 63 | op.open(); 64 | TestUtil.compareDbIterators(op, expectedOut); 65 | op.close(); 66 | } 67 | 68 | /** 69 | * Unit test for Filter.getNext() using a < predicate that filters 70 | * everything 71 | */ 72 | @Test public void filterAllLessThan() throws Exception { 73 | Predicate pred; 74 | pred = new Predicate(0, Predicate.Op.LESS_THAN, TestUtil.getField(-5)); 75 | Filter op = new Filter(pred, scan); 76 | op.open(); 77 | assertTrue(TestUtil.checkExhausted(op)); 78 | op.close(); 79 | } 80 | 81 | /** 82 | * Unit test for Filter.getNext() using an = predicate 83 | */ 84 | @Test public void filterEqual() throws Exception { 85 | Predicate pred; 86 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 87 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(-5)); 88 | Filter op = new Filter(pred, scan); 89 | op.open(); 90 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(-5, testWidth), 91 | op.next())); 92 | op.close(); 93 | 94 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 95 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 96 | op = new Filter(pred, scan); 97 | op.open(); 98 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(0, testWidth), 99 | op.next())); 100 | op.close(); 101 | 102 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 103 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(4)); 104 | op = new Filter(pred, scan); 105 | op.open(); 106 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(4, testWidth), 107 | op.next())); 108 | op.close(); 109 | } 110 | 111 | /** 112 | * Unit test for Filter.getNext() using an = predicate passing no tuples 113 | */ 114 | @Test public void filterEqualNoTuples() throws Exception { 115 | Predicate pred; 116 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(5)); 117 | Filter op = new Filter(pred, scan); 118 | op.open(); 119 | TestUtil.checkExhausted(op); 120 | op.close(); 121 | } 122 | 123 | /** 124 | * JUnit suite target 125 | */ 126 | public static junit.framework.Test suite() { 127 | return new JUnit4TestAdapter(FilterTest.class); 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /test/simpledb/HeapFileReadTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.systemtest.SimpleDbTestBase; 4 | import simpledb.systemtest.SystemTestUtil; 5 | 6 | import java.util.*; 7 | import org.junit.After; 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import static org.junit.Assert.*; 12 | import junit.framework.JUnit4TestAdapter; 13 | 14 | public class HeapFileReadTest extends SimpleDbTestBase { 15 | private HeapFile hf; 16 | private TransactionId tid; 17 | private TupleDesc td; 18 | 19 | /** 20 | * Set up initial resources for each unit test. 21 | */ 22 | @Before 23 | public void setUp() throws Exception { 24 | hf = SystemTestUtil.createRandomHeapFile(2, 20, null, null); 25 | td = Utility.getTupleDesc(2); 26 | tid = new TransactionId(); 27 | } 28 | 29 | @After 30 | public void tearDown() throws Exception { 31 | Database.getBufferPool().transactionComplete(tid); 32 | } 33 | 34 | /** 35 | * Unit test for HeapFile.getId() 36 | */ 37 | @Test 38 | public void getId() throws Exception { 39 | int id = hf.getId(); 40 | 41 | // NOTE(ghuo): the value could be anything. test determinism, at least. 42 | assertEquals(id, hf.getId()); 43 | assertEquals(id, hf.getId()); 44 | 45 | HeapFile other = SystemTestUtil.createRandomHeapFile(1, 1, null, null); 46 | assertTrue(id != other.getId()); 47 | } 48 | 49 | /** 50 | * Unit test for HeapFile.getTupleDesc() 51 | */ 52 | @Test 53 | public void getTupleDesc() throws Exception { 54 | assertEquals(td, hf.getTupleDesc()); 55 | } 56 | /** 57 | * Unit test for HeapFile.numPages() 58 | */ 59 | @Test 60 | public void numPages() throws Exception { 61 | assertEquals(1, hf.numPages()); 62 | // assertEquals(1, empty.numPages()); 63 | } 64 | 65 | /** 66 | * Unit test for HeapFile.readPage() 67 | */ 68 | @Test 69 | public void readPage() throws Exception { 70 | HeapPageId pid = new HeapPageId(hf.getId(), 0); 71 | HeapPage page = (HeapPage) hf.readPage(pid); 72 | 73 | // NOTE(ghuo): we try not to dig too deeply into the Page API here; we 74 | // rely on HeapPageTest for that. perform some basic checks. 75 | assertEquals(484, page.getNumEmptySlots()); 76 | assertTrue(page.isSlotUsed(1)); 77 | assertFalse(page.isSlotUsed(20)); 78 | } 79 | 80 | @Test 81 | public void testIteratorBasic() throws Exception { 82 | HeapFile smallFile = SystemTestUtil.createRandomHeapFile(2, 3, null, 83 | null); 84 | 85 | DbFileIterator it = smallFile.iterator(tid); 86 | // Not open yet 87 | assertFalse(it.hasNext()); 88 | try { 89 | it.next(); 90 | fail("expected exception"); 91 | } catch (NoSuchElementException e) { 92 | } 93 | 94 | it.open(); 95 | int count = 0; 96 | while (it.hasNext()) { 97 | assertNotNull(it.next()); 98 | count += 1; 99 | } 100 | assertEquals(3, count); 101 | it.close(); 102 | } 103 | 104 | @Test 105 | public void testIteratorClose() throws Exception { 106 | // make more than 1 page. Previous closed iterator would start fetching 107 | // from page 1. 108 | HeapFile twoPageFile = SystemTestUtil.createRandomHeapFile(2, 520, 109 | null, null); 110 | 111 | DbFileIterator it = twoPageFile.iterator(tid); 112 | it.open(); 113 | assertTrue(it.hasNext()); 114 | it.close(); 115 | try { 116 | it.next(); 117 | fail("expected exception"); 118 | } catch (NoSuchElementException e) { 119 | } 120 | // close twice is harmless 121 | it.close(); 122 | } 123 | 124 | /** 125 | * JUnit suite target 126 | */ 127 | public static junit.framework.Test suite() { 128 | return new JUnit4TestAdapter(HeapFileReadTest.class); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test/simpledb/HeapFileWriteTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.After; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | import junit.framework.JUnit4TestAdapter; 9 | 10 | public class HeapFileWriteTest extends TestUtil.CreateHeapFile { 11 | private TransactionId tid; 12 | 13 | /** 14 | * Set up initial resources for each unit test. 15 | */ 16 | @Before public void setUp() throws Exception { 17 | super.setUp(); 18 | tid = new TransactionId(); 19 | } 20 | 21 | @After public void tearDown() throws Exception { 22 | Database.getBufferPool().transactionComplete(tid); 23 | } 24 | 25 | /** 26 | * Unit test for HeapFile.addTuple() 27 | */ 28 | @Test public void addTuple() throws Exception { 29 | // we should be able to add 504 tuples on an empty page. 30 | for (int i = 0; i < 504; ++i) { 31 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 32 | assertEquals(1, empty.numPages()); 33 | } 34 | 35 | // the next 512 additions should live on a new page 36 | for (int i = 0; i < 504; ++i) { 37 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 38 | assertEquals(2, empty.numPages()); 39 | } 40 | 41 | // and one more, just for fun... 42 | empty.insertTuple(tid, Utility.getHeapTuple(0, 2)); 43 | assertEquals(3, empty.numPages()); 44 | } 45 | 46 | /** 47 | * JUnit suite target 48 | */ 49 | public static junit.framework.Test suite() { 50 | return new JUnit4TestAdapter(HeapFileWriteTest.class); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /test/simpledb/HeapPageIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import junit.framework.JUnit4TestAdapter; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import simpledb.systemtest.SimpleDbTestBase; 12 | 13 | public class HeapPageIdTest extends SimpleDbTestBase { 14 | 15 | private HeapPageId pid; 16 | 17 | @Before public void createPid() { 18 | pid = new HeapPageId(1, 1); 19 | } 20 | 21 | /** 22 | * Unit test for HeapPageId.getTableId() 23 | */ 24 | @Test public void getTableId() { 25 | assertEquals(1, pid.getTableId()); 26 | } 27 | 28 | /** 29 | * Unit test for HeapPageId.pageno() 30 | */ 31 | @Test public void pageno() { 32 | assertEquals(1, pid.getPageNumber()); 33 | } 34 | 35 | /** 36 | * Unit test for HeapPageId.hashCode() 37 | */ 38 | @Test public void testHashCode() { 39 | int code1, code2; 40 | 41 | // NOTE(ghuo): the hashCode could be anything. test determinism, 42 | // at least. 43 | pid = new HeapPageId(1, 1); 44 | code1 = pid.hashCode(); 45 | assertEquals(code1, pid.hashCode()); 46 | assertEquals(code1, pid.hashCode()); 47 | 48 | pid = new HeapPageId(2, 2); 49 | code2 = pid.hashCode(); 50 | assertEquals(code2, pid.hashCode()); 51 | assertEquals(code2, pid.hashCode()); 52 | } 53 | 54 | /** 55 | * Unit test for HeapPageId.equals() 56 | */ 57 | @Test public void equals() { 58 | HeapPageId pid1 = new HeapPageId(1, 1); 59 | HeapPageId pid1Copy = new HeapPageId(1, 1); 60 | HeapPageId pid2 = new HeapPageId(2, 2); 61 | 62 | // .equals() with null should return false 63 | assertFalse(pid1.equals(null)); 64 | 65 | // .equals() with the wrong type should return false 66 | assertFalse(pid1.equals(new Object())); 67 | 68 | assertTrue(pid1.equals(pid1)); 69 | assertTrue(pid1.equals(pid1Copy)); 70 | assertTrue(pid1Copy.equals(pid1)); 71 | assertTrue(pid2.equals(pid2)); 72 | 73 | assertFalse(pid1.equals(pid2)); 74 | assertFalse(pid1Copy.equals(pid2)); 75 | assertFalse(pid2.equals(pid1)); 76 | assertFalse(pid2.equals(pid1Copy)); 77 | } 78 | 79 | /** 80 | * JUnit suite target 81 | */ 82 | public static junit.framework.Test suite() { 83 | return new JUnit4TestAdapter(HeapPageIdTest.class); 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /test/simpledb/HeapPageReadTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.TestUtil.SkeletonFile; 4 | import simpledb.systemtest.SimpleDbTestBase; 5 | import simpledb.systemtest.SystemTestUtil; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.*; 10 | 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import static org.junit.Assert.assertEquals; 14 | import static org.junit.Assert.assertFalse; 15 | import static org.junit.Assert.assertTrue; 16 | import junit.framework.JUnit4TestAdapter; 17 | 18 | public class HeapPageReadTest extends SimpleDbTestBase { 19 | private HeapPageId pid; 20 | 21 | public static final int[][] EXAMPLE_VALUES = new int[][] { 22 | { 31933, 862 }, 23 | { 29402, 56883 }, 24 | { 1468, 5825 }, 25 | { 17876, 52278 }, 26 | { 6350, 36090 }, 27 | { 34784, 43771 }, 28 | { 28617, 56874 }, 29 | { 19209, 23253 }, 30 | { 56462, 24979 }, 31 | { 51440, 56685 }, 32 | { 3596, 62307 }, 33 | { 45569, 2719 }, 34 | { 22064, 43575 }, 35 | { 42812, 44947 }, 36 | { 22189, 19724 }, 37 | { 33549, 36554 }, 38 | { 9086, 53184 }, 39 | { 42878, 33394 }, 40 | { 62778, 21122 }, 41 | { 17197, 16388 } 42 | }; 43 | 44 | public static final byte[] EXAMPLE_DATA; 45 | static { 46 | // Build the input table 47 | ArrayList> table = new ArrayList>(); 48 | for (int[] tuple : EXAMPLE_VALUES) { 49 | ArrayList listTuple = new ArrayList(); 50 | for (int value : tuple) { 51 | listTuple.add(value); 52 | } 53 | table.add(listTuple); 54 | } 55 | 56 | // Convert it to a HeapFile and read in the bytes 57 | try { 58 | File temp = File.createTempFile("table", ".dat"); 59 | temp.deleteOnExit(); 60 | HeapFileEncoder.convert(table, temp, BufferPool.getPageSize(), 2); 61 | EXAMPLE_DATA = TestUtil.readFileBytes(temp.getAbsolutePath()); 62 | } catch (IOException e) { 63 | throw new RuntimeException(e); 64 | } 65 | } 66 | 67 | /** 68 | * Set up initial resources for each unit test. 69 | */ 70 | @Before public void addTable() throws Exception { 71 | this.pid = new HeapPageId(-1, -1); 72 | Database.getCatalog().addTable(new SkeletonFile(-1, Utility.getTupleDesc(2)), SystemTestUtil.getUUID()); 73 | } 74 | 75 | /** 76 | * Unit test for HeapPage.getId() 77 | */ 78 | @Test public void getId() throws Exception { 79 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 80 | assertEquals(pid, page.getId()); 81 | } 82 | 83 | /** 84 | * Unit test for HeapPage.iterator() 85 | */ 86 | @Test public void testIterator() throws Exception { 87 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 88 | Iterator it = page.iterator(); 89 | 90 | int row = 0; 91 | while (it.hasNext()) { 92 | Tuple tup = it.next(); 93 | IntField f0 = (IntField) tup.getField(0); 94 | IntField f1 = (IntField) tup.getField(1); 95 | 96 | assertEquals(EXAMPLE_VALUES[row][0], f0.getValue()); 97 | assertEquals(EXAMPLE_VALUES[row][1], f1.getValue()); 98 | row++; 99 | } 100 | } 101 | 102 | /** 103 | * Unit test for HeapPage.getNumEmptySlots() 104 | */ 105 | @Test public void getNumEmptySlots() throws Exception { 106 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 107 | assertEquals(484, page.getNumEmptySlots()); 108 | } 109 | 110 | /** 111 | * Unit test for HeapPage.isSlotUsed() 112 | */ 113 | @Test public void getSlot() throws Exception { 114 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 115 | 116 | for (int i = 0; i < 20; ++i) 117 | assertTrue(page.isSlotUsed(i)); 118 | 119 | for (int i = 20; i < 504; ++i) 120 | assertFalse(page.isSlotUsed(i)); 121 | } 122 | 123 | /** 124 | * JUnit suite target 125 | */ 126 | public static junit.framework.Test suite() { 127 | return new JUnit4TestAdapter(HeapPageReadTest.class); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /test/simpledb/HeapPageWriteTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.io.IOException; 7 | import java.util.Iterator; 8 | import java.util.LinkedList; 9 | 10 | import junit.framework.JUnit4TestAdapter; 11 | 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | 15 | import simpledb.TestUtil.SkeletonFile; 16 | import simpledb.systemtest.SimpleDbTestBase; 17 | import simpledb.systemtest.SystemTestUtil; 18 | 19 | public class HeapPageWriteTest extends SimpleDbTestBase { 20 | 21 | private HeapPageId pid; 22 | 23 | /** 24 | * Set up initial resources for each unit test. 25 | */ 26 | @Before public void addTable() throws IOException { 27 | this.pid = new HeapPageId(-1, -1); 28 | Database.getCatalog().addTable(new SkeletonFile(-1, Utility.getTupleDesc(2)), SystemTestUtil.getUUID()); 29 | } 30 | 31 | /** 32 | * Unit test for HeapPage.isDirty() 33 | */ 34 | @Test public void testDirty() throws Exception { 35 | TransactionId tid = new TransactionId(); 36 | HeapPage page = new HeapPage(pid, HeapPageReadTest.EXAMPLE_DATA); 37 | page.markDirty(true, tid); 38 | TransactionId dirtier = page.isDirty(); 39 | assertEquals(true, dirtier != null); 40 | assertEquals(true, dirtier == tid); 41 | 42 | page.markDirty(false, tid); 43 | dirtier = page.isDirty(); 44 | assertEquals(false, dirtier != null); 45 | } 46 | 47 | /** 48 | * Unit test for HeapPage.addTuple() 49 | */ 50 | @Test public void addTuple() throws Exception { 51 | HeapPage page = new HeapPage(pid, HeapPageReadTest.EXAMPLE_DATA); 52 | int free = page.getNumEmptySlots(); 53 | 54 | // NOTE(ghuo): this nested loop existence check is slow, but it 55 | // shouldn't make a difference for n = 504 slots. 56 | 57 | for (int i = 0; i < free; ++i) { 58 | Tuple addition = Utility.getHeapTuple(i, 2); 59 | page.insertTuple(addition); 60 | assertEquals(free-i-1, page.getNumEmptySlots()); 61 | 62 | // loop through the iterator to ensure that the tuple actually exists 63 | // on the page 64 | Iteratorit = page.iterator(); 65 | boolean found = false; 66 | while (it.hasNext()) { 67 | Tuple tup = it.next(); 68 | if (TestUtil.compareTuples(addition, tup)) { 69 | found = true; 70 | 71 | // verify that the RecordId is sane 72 | assertEquals(page.getId(), tup.getRecordId().getPageId()); 73 | break; 74 | } 75 | } 76 | assertTrue(found); 77 | } 78 | 79 | // now, the page should be full. 80 | try { 81 | page.insertTuple(Utility.getHeapTuple(0, 2)); 82 | throw new Exception("page should be full; expected DbException"); 83 | } catch (DbException e) { 84 | // explicitly ignored 85 | } 86 | } 87 | 88 | /** 89 | * Unit test for HeapPage.deleteTuple() with false tuples 90 | */ 91 | @Test(expected=DbException.class) 92 | public void deleteNonexistentTuple() throws Exception { 93 | HeapPage page = new HeapPage(pid, HeapPageReadTest.EXAMPLE_DATA); 94 | page.deleteTuple(Utility.getHeapTuple(2, 2)); 95 | } 96 | 97 | /** 98 | * Unit test for HeapPage.deleteTuple() 99 | */ 100 | @Test public void deleteTuple() throws Exception { 101 | HeapPage page = new HeapPage(pid, HeapPageReadTest.EXAMPLE_DATA); 102 | int free = page.getNumEmptySlots(); 103 | 104 | // first, build a list of the tuples on the page. 105 | Iterator it = page.iterator(); 106 | LinkedList tuples = new LinkedList(); 107 | while (it.hasNext()) 108 | tuples.add(it.next()); 109 | Tuple first = tuples.getFirst(); 110 | 111 | // now, delete them one-by-one from both the front and the end. 112 | int deleted = 0; 113 | while (tuples.size() > 0) { 114 | page.deleteTuple(tuples.removeFirst()); 115 | page.deleteTuple(tuples.removeLast()); 116 | deleted += 2; 117 | assertEquals(free + deleted, page.getNumEmptySlots()); 118 | } 119 | 120 | // now, the page should be empty. 121 | try { 122 | page.deleteTuple(first); 123 | throw new Exception("page should be empty; expected DbException"); 124 | } catch (DbException e) { 125 | // explicitly ignored 126 | } 127 | } 128 | 129 | /** 130 | * JUnit suite target 131 | */ 132 | public static junit.framework.Test suite() { 133 | return new JUnit4TestAdapter(HeapPageWriteTest.class); 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /test/simpledb/InsertTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | import static org.junit.Assert.assertTrue; 8 | import junit.framework.JUnit4TestAdapter; 9 | 10 | /** 11 | * We reserve more heavy-duty insertion testing for HeapFile and HeapPage. 12 | * This suite is superficial. 13 | */ 14 | public class InsertTest extends TestUtil.CreateHeapFile { 15 | 16 | private OpIterator scan1; 17 | private TransactionId tid; 18 | 19 | /** 20 | * Initialize each unit test 21 | */ 22 | @Before public void setUp() throws Exception { 23 | super.setUp(); 24 | this.scan1 = TestUtil.createTupleList(2, 25 | new int[] { 1, 2, 26 | 1, 4, 27 | 1, 6, 28 | 3, 2, 29 | 3, 4, 30 | 3, 6, 31 | 5, 7 }); 32 | tid = new TransactionId(); 33 | } 34 | 35 | /** 36 | * Unit test for Insert.getTupleDesc() 37 | */ 38 | @Test public void getTupleDesc() throws Exception { 39 | Insert op = new Insert(tid,scan1, empty.getId()); 40 | TupleDesc expected = Utility.getTupleDesc(1); 41 | TupleDesc actual = op.getTupleDesc(); 42 | assertEquals(expected, actual); 43 | } 44 | 45 | /** 46 | * Unit test for Insert.getNext(), inserting elements into an empty file 47 | */ 48 | @Test public void getNext() throws Exception { 49 | Insert op = new Insert(tid,scan1, empty.getId()); 50 | op.open(); 51 | assertTrue(TestUtil.compareTuples( 52 | Utility.getHeapTuple(7, 1), // the length of scan1 53 | op.next())); 54 | 55 | // we should fit on one page 56 | assertEquals(1, empty.numPages()); 57 | } 58 | 59 | /** 60 | * JUnit suite target 61 | */ 62 | public static junit.framework.Test suite() { 63 | return new JUnit4TestAdapter(InsertTest.class); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /test/simpledb/JoinPredicateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Test; 4 | 5 | import simpledb.systemtest.SimpleDbTestBase; 6 | import static org.junit.Assert.assertTrue; 7 | import static org.junit.Assert.assertFalse; 8 | import junit.framework.JUnit4TestAdapter; 9 | 10 | public class JoinPredicateTest extends SimpleDbTestBase { 11 | 12 | /** 13 | * Unit test for JoinPredicate.filter() 14 | */ 15 | @Test public void filterVaryingVals() { 16 | int[] vals = new int[] { -1, 0, 1 }; 17 | 18 | for (int i : vals) { 19 | JoinPredicate p = new JoinPredicate(0, 20 | Predicate.Op.EQUALS, 0); 21 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 22 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 23 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 24 | } 25 | 26 | for (int i : vals) { 27 | JoinPredicate p = new JoinPredicate(0, 28 | Predicate.Op.GREATER_THAN, 0); 29 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 30 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 31 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 32 | } 33 | 34 | for (int i : vals) { 35 | JoinPredicate p = new JoinPredicate(0, 36 | Predicate.Op.GREATER_THAN_OR_EQ, 0); 37 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 38 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 39 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 40 | } 41 | 42 | for (int i : vals) { 43 | JoinPredicate p = new JoinPredicate(0, 44 | Predicate.Op.LESS_THAN, 0); 45 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 46 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 47 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 48 | } 49 | 50 | for (int i : vals) { 51 | JoinPredicate p = new JoinPredicate(0, 52 | Predicate.Op.LESS_THAN_OR_EQ, 0); 53 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 54 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 55 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 56 | } 57 | } 58 | 59 | /** 60 | * JUnit suite target 61 | */ 62 | public static junit.framework.Test suite() { 63 | return new JUnit4TestAdapter(JoinPredicateTest.class); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /test/simpledb/JoinTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertTrue; 6 | import junit.framework.JUnit4TestAdapter; 7 | 8 | import org.junit.Before; 9 | import org.junit.Test; 10 | 11 | import simpledb.systemtest.SimpleDbTestBase; 12 | 13 | public class JoinTest extends SimpleDbTestBase { 14 | 15 | int width1 = 2; 16 | int width2 = 3; 17 | OpIterator scan1; 18 | OpIterator scan2; 19 | OpIterator eqJoin; 20 | OpIterator gtJoin; 21 | 22 | /** 23 | * Initialize each unit test 24 | */ 25 | @Before public void createTupleLists() throws Exception { 26 | this.scan1 = TestUtil.createTupleList(width1, 27 | new int[] { 1, 2, 28 | 3, 4, 29 | 5, 6, 30 | 7, 8 }); 31 | this.scan2 = TestUtil.createTupleList(width2, 32 | new int[] { 1, 2, 3, 33 | 2, 3, 4, 34 | 3, 4, 5, 35 | 4, 5, 6, 36 | 5, 6, 7 }); 37 | this.eqJoin = TestUtil.createTupleList(width1 + width2, 38 | new int[] { 1, 2, 1, 2, 3, 39 | 3, 4, 3, 4, 5, 40 | 5, 6, 5, 6, 7 }); 41 | this.gtJoin = TestUtil.createTupleList(width1 + width2, 42 | new int[] { 43 | 3, 4, 1, 2, 3, // 1, 2 < 3 44 | 3, 4, 2, 3, 4, 45 | 5, 6, 1, 2, 3, // 1, 2, 3, 4 < 5 46 | 5, 6, 2, 3, 4, 47 | 5, 6, 3, 4, 5, 48 | 5, 6, 4, 5, 6, 49 | 7, 8, 1, 2, 3, // 1, 2, 3, 4, 5 < 7 50 | 7, 8, 2, 3, 4, 51 | 7, 8, 3, 4, 5, 52 | 7, 8, 4, 5, 6, 53 | 7, 8, 5, 6, 7 }); 54 | } 55 | 56 | /** 57 | * Unit test for Join.getTupleDesc() 58 | */ 59 | @Test public void getTupleDesc() { 60 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 61 | Join op = new Join(pred, scan1, scan2); 62 | TupleDesc expected = Utility.getTupleDesc(width1 + width2); 63 | TupleDesc actual = op.getTupleDesc(); 64 | assertEquals(expected, actual); 65 | } 66 | 67 | /** 68 | * Unit test for Join.rewind() 69 | */ 70 | @Test public void rewind() throws Exception { 71 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 72 | Join op = new Join(pred, scan1, scan2); 73 | op.open(); 74 | while (op.hasNext()) { 75 | assertNotNull(op.next()); 76 | } 77 | assertTrue(TestUtil.checkExhausted(op)); 78 | op.rewind(); 79 | 80 | eqJoin.open(); 81 | Tuple expected = eqJoin.next(); 82 | Tuple actual = op.next(); 83 | assertTrue(TestUtil.compareTuples(expected, actual)); 84 | } 85 | 86 | /** 87 | * Unit test for Join.getNext() using a > predicate 88 | */ 89 | @Test public void gtJoin() throws Exception { 90 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.GREATER_THAN, 0); 91 | Join op = new Join(pred, scan1, scan2); 92 | op.open(); 93 | gtJoin.open(); 94 | TestUtil.matchAllTuples(gtJoin, op); 95 | } 96 | 97 | /** 98 | * Unit test for Join.getNext() using an = predicate 99 | */ 100 | @Test public void eqJoin() throws Exception { 101 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 102 | Join op = new Join(pred, scan1, scan2); 103 | op.open(); 104 | eqJoin.open(); 105 | TestUtil.matchAllTuples(eqJoin, op); 106 | } 107 | 108 | /** 109 | * JUnit suite target 110 | */ 111 | public static junit.framework.Test suite() { 112 | return new JUnit4TestAdapter(JoinTest.class); 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /test/simpledb/PredicateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Test; 4 | 5 | import simpledb.systemtest.SimpleDbTestBase; 6 | import static org.junit.Assert.assertTrue; 7 | import static org.junit.Assert.assertFalse; 8 | import junit.framework.JUnit4TestAdapter; 9 | 10 | public class PredicateTest extends SimpleDbTestBase{ 11 | 12 | /** 13 | * Unit test for Predicate.filter() 14 | */ 15 | @Test public void filter() { 16 | int[] vals = new int[] { -1, 0, 1 }; 17 | 18 | for (int i : vals) { 19 | Predicate p = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(i)); 20 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 21 | assertTrue(p.filter(Utility.getHeapTuple(i))); 22 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 23 | } 24 | 25 | for (int i : vals) { 26 | Predicate p = new Predicate(0, Predicate.Op.GREATER_THAN, 27 | TestUtil.getField(i)); 28 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 29 | assertFalse(p.filter(Utility.getHeapTuple(i))); 30 | assertTrue(p.filter(Utility.getHeapTuple(i + 1))); 31 | } 32 | 33 | for (int i : vals) { 34 | Predicate p = new Predicate(0, Predicate.Op.GREATER_THAN_OR_EQ, 35 | TestUtil.getField(i)); 36 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 37 | assertTrue(p.filter(Utility.getHeapTuple(i))); 38 | assertTrue(p.filter(Utility.getHeapTuple(i + 1))); 39 | } 40 | 41 | for (int i : vals) { 42 | Predicate p = new Predicate(0, Predicate.Op.LESS_THAN, 43 | TestUtil.getField(i)); 44 | assertTrue(p.filter(Utility.getHeapTuple(i - 1))); 45 | assertFalse(p.filter(Utility.getHeapTuple(i))); 46 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 47 | } 48 | 49 | for (int i : vals) { 50 | Predicate p = new Predicate(0, Predicate.Op.LESS_THAN_OR_EQ, 51 | TestUtil.getField(i)); 52 | assertTrue(p.filter(Utility.getHeapTuple(i - 1))); 53 | assertTrue(p.filter(Utility.getHeapTuple(i))); 54 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 55 | } 56 | } 57 | 58 | /** 59 | * JUnit suite target 60 | */ 61 | public static junit.framework.Test suite() { 62 | return new JUnit4TestAdapter(PredicateTest.class); 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /test/simpledb/RecordIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import junit.framework.JUnit4TestAdapter; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import simpledb.systemtest.SimpleDbTestBase; 11 | 12 | public class RecordIdTest extends SimpleDbTestBase { 13 | 14 | private static RecordId hrid; 15 | private static RecordId hrid2; 16 | private static RecordId hrid3; 17 | private static RecordId hrid4; 18 | 19 | @Before public void createPids() { 20 | HeapPageId hpid = new HeapPageId(-1, 2); 21 | HeapPageId hpid2 = new HeapPageId(-1, 2); 22 | HeapPageId hpid3 = new HeapPageId(-2, 2); 23 | hrid = new RecordId(hpid, 3); 24 | hrid2 = new RecordId(hpid2, 3); 25 | hrid3 = new RecordId(hpid, 4); 26 | hrid4 = new RecordId(hpid3, 3); 27 | 28 | } 29 | 30 | /** 31 | * Unit test for RecordId.getPageId() 32 | */ 33 | @Test public void getPageId() { 34 | HeapPageId hpid = new HeapPageId(-1, 2); 35 | assertEquals(hpid, hrid.getPageId()); 36 | 37 | } 38 | 39 | /** 40 | * Unit test for RecordId.getTupleNumber() 41 | */ 42 | @Test public void tupleno() { 43 | assertEquals(3, hrid.getTupleNumber()); 44 | } 45 | 46 | /** 47 | * Unit test for RecordId.equals() 48 | */ 49 | @Test public void equals() { 50 | assertEquals(hrid, hrid2); 51 | assertEquals(hrid2, hrid); 52 | assertFalse(hrid.equals(hrid3)); 53 | assertFalse(hrid3.equals(hrid)); 54 | assertFalse(hrid2.equals(hrid4)); 55 | assertFalse(hrid4.equals(hrid2)); 56 | } 57 | 58 | /** 59 | * Unit test for RecordId.hashCode() 60 | */ 61 | @Test public void hCode() { 62 | assertEquals(hrid.hashCode(), hrid2.hashCode()); 63 | } 64 | 65 | /** 66 | * JUnit suite target 67 | */ 68 | public static junit.framework.Test suite() { 69 | return new JUnit4TestAdapter(RecordIdTest.class); 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /test/simpledb/StringAggregatorTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import simpledb.systemtest.SimpleDbTestBase; 9 | import static org.junit.Assert.assertEquals; 10 | import junit.framework.JUnit4TestAdapter; 11 | 12 | public class StringAggregatorTest extends SimpleDbTestBase { 13 | 14 | int width1 = 2; 15 | OpIterator scan1; 16 | int[][] count = null; 17 | 18 | /** 19 | * Initialize each unit test 20 | */ 21 | @Before public void createTupleList() throws Exception { 22 | this.scan1 = TestUtil.createTupleList(width1, 23 | new Object[] { 1, "a", 24 | 1, "b", 25 | 1, "c", 26 | 3, "d", 27 | 3, "e", 28 | 3, "f", 29 | 5, "g" }); 30 | 31 | // verify how the results progress after a few merges 32 | this.count = new int[][] { 33 | { 1, 1 }, 34 | { 1, 2 }, 35 | { 1, 3 }, 36 | { 1, 3, 3, 1 } 37 | }; 38 | 39 | } 40 | 41 | /** 42 | * Test String.mergeTupleIntoGroup() and iterator() over a COUNT 43 | */ 44 | @Test public void mergeCount() throws Exception { 45 | scan1.open(); 46 | StringAggregator agg = new StringAggregator(0, Type.INT_TYPE, 1, Aggregator.Op.COUNT); 47 | 48 | for (int[] step : count) { 49 | agg.mergeTupleIntoGroup(scan1.next()); 50 | OpIterator it = agg.iterator(); 51 | it.open(); 52 | TestUtil.matchAllTuples(TestUtil.createTupleList(width1, step), it); 53 | } 54 | } 55 | 56 | /** 57 | * Test StringAggregator.iterator() for OpIterator behaviour 58 | */ 59 | @Test public void testIterator() throws Exception { 60 | // first, populate the aggregator via sum over scan1 61 | scan1.open(); 62 | StringAggregator agg = new StringAggregator(0, Type.INT_TYPE, 1, Aggregator.Op.COUNT); 63 | try { 64 | while (true) 65 | agg.mergeTupleIntoGroup(scan1.next()); 66 | } catch (NoSuchElementException e) { 67 | // explicitly ignored 68 | } 69 | 70 | OpIterator it = agg.iterator(); 71 | it.open(); 72 | 73 | // verify it has three elements 74 | int count = 0; 75 | try { 76 | while (true) { 77 | it.next(); 78 | count++; 79 | } 80 | } catch (NoSuchElementException e) { 81 | // explicitly ignored 82 | } 83 | assertEquals(3, count); 84 | 85 | // rewind and try again 86 | it.rewind(); 87 | count = 0; 88 | try { 89 | while (true) { 90 | it.next(); 91 | count++; 92 | } 93 | } catch (NoSuchElementException e) { 94 | // explicitly ignored 95 | } 96 | assertEquals(3, count); 97 | 98 | // close it and check that we don't get anything 99 | it.close(); 100 | try { 101 | it.next(); 102 | throw new Exception("StringAggreator iterator yielded tuple after close"); 103 | } catch (Exception e) { 104 | // explicitly ignored 105 | } 106 | } 107 | 108 | /** 109 | * JUnit suite target 110 | */ 111 | public static junit.framework.Test suite() { 112 | return new JUnit4TestAdapter(StringAggregatorTest.class); 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /test/simpledb/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.util.*; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import static org.junit.Assert.assertEquals; 7 | import junit.framework.JUnit4TestAdapter; 8 | 9 | public class TransactionTest extends TestUtil.CreateHeapFile { 10 | private PageId p0, p1, p2; 11 | private TransactionId tid1, tid2; 12 | 13 | // just so we have a pointer shorter than Database.getBufferPool() 14 | private BufferPool bp; 15 | 16 | /** 17 | * Set up initial resources for each unit test. 18 | */ 19 | @Before public void setUp() throws Exception { 20 | super.setUp(); 21 | 22 | // clear all state from the buffer pool 23 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 24 | 25 | // create a new empty HeapFile and populate it with three pages. 26 | // we should be able to add 504 tuples on an empty page. 27 | TransactionId tid = new TransactionId(); 28 | for (int i = 0; i < 1025; ++i) { 29 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 30 | } 31 | 32 | // if this fails, complain to the TA 33 | assertEquals(3, empty.numPages()); 34 | 35 | this.p0 = new HeapPageId(empty.getId(), 0); 36 | this.p1 = new HeapPageId(empty.getId(), 1); 37 | this.p2 = new HeapPageId(empty.getId(), 2); 38 | this.tid1 = new TransactionId(); 39 | this.tid2 = new TransactionId(); 40 | 41 | // forget about locks associated to tid, so they don't conflict with 42 | // test cases 43 | bp.getPage(tid, p0, Permissions.READ_WRITE).markDirty(true, tid); 44 | bp.getPage(tid, p1, Permissions.READ_WRITE).markDirty(true, tid); 45 | bp.getPage(tid, p2, Permissions.READ_WRITE).markDirty(true, tid); 46 | bp.flushAllPages(); 47 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 48 | } 49 | 50 | /** 51 | * Unit test for BufferPool.transactionComplete(). 52 | * Try to acquire locks that would conflict if old locks aren't released 53 | * during transactionComplete(). 54 | */ 55 | @Test public void attemptTransactionTwice() throws Exception { 56 | bp.getPage(tid1, p0, Permissions.READ_ONLY); 57 | bp.getPage(tid1, p1, Permissions.READ_WRITE); 58 | bp.transactionComplete(tid1, true); 59 | 60 | bp.getPage(tid2, p0, Permissions.READ_WRITE); 61 | bp.getPage(tid2, p0, Permissions.READ_WRITE); 62 | } 63 | 64 | /** 65 | * Common unit test code for BufferPool.transactionComplete() covering 66 | * commit and abort. Verify that commit persists changes to disk, and 67 | * that abort reverts pages to their previous on-disk state. 68 | */ 69 | public void testTransactionComplete(boolean commit) throws Exception { 70 | HeapPage p = (HeapPage) bp.getPage(tid1, p2, Permissions.READ_WRITE); 71 | 72 | Tuple t = Utility.getHeapTuple(new int[] { 6, 830 }); 73 | t.setRecordId(new RecordId(p2, 1)); 74 | 75 | p.insertTuple(t); 76 | p.markDirty(true, tid1); 77 | bp.transactionComplete(tid1, commit); 78 | 79 | // now, flush the buffer pool and access the page again from disk. 80 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 81 | p = (HeapPage) bp.getPage(tid2, p2, Permissions.READ_WRITE); 82 | Iterator it = p.iterator(); 83 | 84 | boolean found = false; 85 | while (it.hasNext()) { 86 | Tuple tup = (Tuple) it.next(); 87 | IntField f0 = (IntField) tup.getField(0); 88 | IntField f1 = (IntField) tup.getField(1); 89 | 90 | if (f0.getValue() == 6 && f1.getValue() == 830) { 91 | found = true; 92 | break; 93 | } 94 | } 95 | 96 | assertEquals(commit, found); 97 | } 98 | 99 | /** 100 | * Unit test for BufferPool.transactionComplete() assuing commit. 101 | * Verify that a tuple inserted during a committed transaction is durable 102 | */ 103 | @Test public void commitTransaction() throws Exception { 104 | testTransactionComplete(true); 105 | } 106 | 107 | /** 108 | * Unit test for BufferPool.transactionComplete() assuming abort. 109 | * Verify that a tuple inserted during a committed transaction is durable 110 | */ 111 | @Test public void abortTransaction() throws Exception { 112 | testTransactionComplete(false); 113 | } 114 | 115 | /** 116 | * JUnit suite target 117 | */ 118 | public static junit.framework.Test suite() { 119 | return new JUnit4TestAdapter(TransactionTest.class); 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /test/simpledb/TupleTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import junit.framework.JUnit4TestAdapter; 5 | 6 | import org.junit.Test; 7 | 8 | import simpledb.systemtest.SimpleDbTestBase; 9 | 10 | public class TupleTest extends SimpleDbTestBase { 11 | 12 | /** 13 | * Unit test for Tuple.getField() and Tuple.setField() 14 | */ 15 | @Test public void modifyFields() { 16 | TupleDesc td = Utility.getTupleDesc(2); 17 | 18 | Tuple tup = new Tuple(td); 19 | tup.setField(0, new IntField(-1)); 20 | tup.setField(1, new IntField(0)); 21 | 22 | assertEquals(new IntField(-1), tup.getField(0)); 23 | assertEquals(new IntField(0), tup.getField(1)); 24 | 25 | tup.setField(0, new IntField(1)); 26 | tup.setField(1, new IntField(37)); 27 | 28 | assertEquals(new IntField(1), tup.getField(0)); 29 | assertEquals(new IntField(37), tup.getField(1)); 30 | } 31 | 32 | /** 33 | * Unit test for Tuple.getTupleDesc() 34 | */ 35 | @Test public void getTupleDesc() { 36 | TupleDesc td = Utility.getTupleDesc(5); 37 | Tuple tup = new Tuple(td); 38 | assertEquals(td, tup.getTupleDesc()); 39 | } 40 | 41 | /** 42 | * Unit test for Tuple.getRecordId() and Tuple.setRecordId() 43 | */ 44 | @Test public void modifyRecordId() { 45 | Tuple tup1 = new Tuple(Utility.getTupleDesc(1)); 46 | HeapPageId pid1 = new HeapPageId(0,0); 47 | RecordId rid1 = new RecordId(pid1, 0); 48 | tup1.setRecordId(rid1); 49 | 50 | try { 51 | assertEquals(rid1, tup1.getRecordId()); 52 | } catch (java.lang.UnsupportedOperationException e) { 53 | //rethrow the exception with an explanation 54 | throw new UnsupportedOperationException("modifyRecordId() test failed due to " + 55 | "RecordId.equals() not being implemented. This is not required for Lab 1, " + 56 | "but should pass when you do implement the RecordId class."); 57 | } 58 | } 59 | 60 | /** 61 | * JUnit suite target 62 | */ 63 | public static junit.framework.Test suite() { 64 | return new JUnit4TestAdapter(TupleTest.class); 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/AbortEvictionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | 5 | import simpledb.*; 6 | 7 | import static org.junit.Assert.*; 8 | import org.junit.Test; 9 | 10 | public class AbortEvictionTest extends SimpleDbTestBase { 11 | /** Aborts a transaction and ensures that its effects were actually undone. 12 | * This requires dirty pages to not get flushed to disk. 13 | */ 14 | @Test public void testDoNotEvictDirtyPages() 15 | throws IOException, DbException, TransactionAbortedException { 16 | // Allocate a file with ~10 pages of data 17 | HeapFile f = SystemTestUtil.createRandomHeapFile(2, 512*10, null, null); 18 | Database.resetBufferPool(2); 19 | 20 | // BEGIN TRANSACTION 21 | Transaction t = new Transaction(); 22 | t.start(); 23 | 24 | // Insert a new row 25 | EvictionTest.insertRow(f, t); 26 | 27 | // The tuple must exist in the table 28 | boolean found = EvictionTest.findMagicTuple(f, t); 29 | assertTrue(found); 30 | // ABORT 31 | t.transactionComplete(true); 32 | 33 | // A second transaction must not find the tuple 34 | t = new Transaction(); 35 | t.start(); 36 | found = EvictionTest.findMagicTuple(f, t); 37 | assertFalse(found); 38 | t.commit(); 39 | } 40 | 41 | /** Make test compatible with older version of ant. */ 42 | public static junit.framework.Test suite() { 43 | return new junit.framework.JUnit4TestAdapter(AbortEvictionTest.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/DeleteTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | import simpledb.*; 8 | 9 | public class DeleteTest extends FilterBase { 10 | ArrayList> expectedTuples = null; 11 | 12 | @Override 13 | protected int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 14 | throws DbException, TransactionAbortedException, IOException { 15 | SeqScan ss = new SeqScan(tid, table.getId(), ""); 16 | Filter filter = new Filter(predicate, ss); 17 | Delete deleteOperator = new Delete(tid, filter); 18 | // Query q = new Query(deleteOperator, tid); 19 | 20 | // q.start(); 21 | deleteOperator.open(); 22 | boolean hasResult = false; 23 | int result = -1; 24 | while (deleteOperator.hasNext()) { 25 | Tuple t = deleteOperator.next(); 26 | assertFalse(hasResult); 27 | hasResult = true; 28 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, t.getTupleDesc()); 29 | result = ((IntField) t.getField(0)).getValue(); 30 | } 31 | assertTrue(hasResult); 32 | 33 | deleteOperator.close(); 34 | 35 | // As part of the same transaction, scan the table 36 | if (result == 0) { 37 | // Deleted zero tuples: all tuples still in table 38 | expectedTuples = createdTuples; 39 | } else { 40 | assert result == createdTuples.size(); 41 | expectedTuples = new ArrayList>(); 42 | } 43 | SystemTestUtil.matchTuples(table, tid, expectedTuples); 44 | return result; 45 | } 46 | 47 | @Override 48 | protected void validateAfter(HeapFile table) 49 | throws DbException, TransactionAbortedException, IOException { 50 | // As part of a different transaction, scan the table 51 | SystemTestUtil.matchTuples(table, expectedTuples); 52 | } 53 | 54 | /** Make test compatible with older version of ant. */ 55 | public static junit.framework.Test suite() { 56 | return new junit.framework.JUnit4TestAdapter(DeleteTest.class); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/EvictionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.io.IOException; 6 | import java.util.Arrays; 7 | 8 | import org.junit.Test; 9 | 10 | import junit.framework.Assert; 11 | import simpledb.*; 12 | 13 | /** 14 | * Creates a heap file with 1024*500 tuples with two integer fields each. Clears the buffer pool, 15 | * and performs a sequential scan through all of the pages. If the growth in JVM usage 16 | * is greater than 2 MB due to the scan, the test fails. Otherwise, the page eviction policy seems 17 | * to have worked. 18 | */ 19 | public class EvictionTest extends SimpleDbTestBase { 20 | private static final long MEMORY_LIMIT_IN_MB = 5; 21 | private static final int BUFFER_PAGES = 16; 22 | 23 | @Test public void testHeapFileScanWithManyPages() throws IOException, DbException, TransactionAbortedException { 24 | System.out.println("EvictionTest creating large table"); 25 | HeapFile f = SystemTestUtil.createRandomHeapFile(2, 1024*500, null, null); 26 | System.out.println("EvictionTest scanning large table"); 27 | Database.resetBufferPool(BUFFER_PAGES); 28 | long beginMem = SystemTestUtil.getMemoryFootprint(); 29 | TransactionId tid = new TransactionId(); 30 | SeqScan scan = new SeqScan(tid, f.getId(), ""); 31 | scan.open(); 32 | while (scan.hasNext()) { 33 | scan.next(); 34 | } 35 | System.out.println("EvictionTest scan complete, testing memory usage of scan"); 36 | long endMem = SystemTestUtil.getMemoryFootprint(); 37 | long memDiff = (endMem - beginMem) / (1<<20); 38 | if (memDiff > MEMORY_LIMIT_IN_MB) { 39 | Assert.fail("Did not evict enough pages. Scan took " + memDiff + " MB of RAM, when limit was " + MEMORY_LIMIT_IN_MB); 40 | } 41 | } 42 | 43 | public static void insertRow(HeapFile f, Transaction t) throws DbException, 44 | TransactionAbortedException { 45 | // Create a row to insert 46 | TupleDesc twoIntColumns = Utility.getTupleDesc(2); 47 | Tuple value = new Tuple(twoIntColumns); 48 | value.setField(0, new IntField(-42)); 49 | value.setField(1, new IntField(-43)); 50 | TupleIterator insertRow = new TupleIterator(Utility.getTupleDesc(2), Arrays.asList(new Tuple[]{value})); 51 | 52 | // Insert the row 53 | Insert insert = new Insert(t.getId(), insertRow, f.getId()); 54 | insert.open(); 55 | Tuple result = insert.next(); 56 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, result.getTupleDesc()); 57 | assertEquals(1, ((IntField)result.getField(0)).getValue()); 58 | assertFalse(insert.hasNext()); 59 | insert.close(); 60 | } 61 | 62 | public static boolean findMagicTuple(HeapFile f, Transaction t) 63 | throws DbException, TransactionAbortedException { 64 | SeqScan ss = new SeqScan(t.getId(), f.getId(), ""); 65 | boolean found = false; 66 | ss.open(); 67 | while (ss.hasNext()) { 68 | Tuple v = ss.next(); 69 | int v0 = ((IntField)v.getField(0)).getValue(); 70 | int v1 = ((IntField)v.getField(1)).getValue(); 71 | if (v0 == -42 && v1 == -43) { 72 | assertFalse(found); 73 | found = true; 74 | } 75 | } 76 | ss.close(); 77 | return found; 78 | } 79 | 80 | /** Make test compatible with older version of ant. */ 81 | public static junit.framework.Test suite() { 82 | return new junit.framework.JUnit4TestAdapter(EvictionTest.class); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/FilterBase.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import static org.junit.Assert.*; 9 | import org.junit.Test; 10 | 11 | import simpledb.*; 12 | 13 | public abstract class FilterBase extends SimpleDbTestBase { 14 | private static final int COLUMNS = 3; 15 | private static final int ROWS = 1097; 16 | 17 | /** Should apply the predicate to table. This will be executed in transaction tid. */ 18 | protected abstract int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 19 | throws DbException, TransactionAbortedException, IOException; 20 | 21 | /** Optional hook for validating database state after applyPredicate. */ 22 | protected void validateAfter(HeapFile table) 23 | throws DbException, TransactionAbortedException, IOException {} 24 | 25 | protected ArrayList> createdTuples; 26 | 27 | private int runTransactionForPredicate(HeapFile table, Predicate predicate) 28 | throws IOException, DbException, TransactionAbortedException { 29 | TransactionId tid = new TransactionId(); 30 | int result = applyPredicate(table, tid, predicate); 31 | Database.getBufferPool().transactionComplete(tid); 32 | return result; 33 | } 34 | 35 | private void validatePredicate(int column, int columnValue, int trueValue, int falseValue, 36 | Predicate.Op operation) throws IOException, DbException, TransactionAbortedException { 37 | // Test the true value 38 | HeapFile f = createTable(column, columnValue); 39 | Predicate predicate = new Predicate(column, operation, new IntField(trueValue)); 40 | assertEquals(ROWS, runTransactionForPredicate(f, predicate)); 41 | f = Utility.openHeapFile(COLUMNS, f.getFile()); 42 | validateAfter(f); 43 | 44 | // Test the false value 45 | f = createTable(column, columnValue); 46 | predicate = new Predicate(column, operation, new IntField(falseValue)); 47 | assertEquals(0, runTransactionForPredicate(f, predicate)); 48 | f = Utility.openHeapFile(COLUMNS, f.getFile()); 49 | validateAfter(f); 50 | } 51 | 52 | private HeapFile createTable(int column, int columnValue) 53 | throws IOException, DbException, TransactionAbortedException { 54 | Map columnSpecification = new HashMap(); 55 | columnSpecification.put(column, columnValue); 56 | createdTuples = new ArrayList>(); 57 | return SystemTestUtil.createRandomHeapFile( 58 | COLUMNS, ROWS, columnSpecification, createdTuples); 59 | } 60 | 61 | @Test public void testEquals() throws 62 | DbException, TransactionAbortedException, IOException { 63 | validatePredicate(0, 1, 1, 2, Predicate.Op.EQUALS); 64 | } 65 | 66 | @Test public void testLessThan() throws 67 | DbException, TransactionAbortedException, IOException { 68 | validatePredicate(1, 1, 2, 1, Predicate.Op.LESS_THAN); 69 | } 70 | 71 | @Test public void testLessThanOrEq() throws 72 | DbException, TransactionAbortedException, IOException { 73 | validatePredicate(2, 42, 42, 41, Predicate.Op.LESS_THAN_OR_EQ); 74 | } 75 | 76 | @Test public void testGreaterThan() throws 77 | DbException, TransactionAbortedException, IOException { 78 | validatePredicate(2, 42, 41, 42, Predicate.Op.GREATER_THAN); 79 | } 80 | 81 | @Test public void testGreaterThanOrEq() throws 82 | DbException, TransactionAbortedException, IOException { 83 | validatePredicate(2, 42, 42, 43, Predicate.Op.GREATER_THAN_OR_EQ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/FilterTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | import static org.junit.Assert.*; 5 | import simpledb.*; 6 | 7 | public class FilterTest extends FilterBase { 8 | @Override 9 | protected int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 10 | throws DbException, TransactionAbortedException, IOException { 11 | SeqScan ss = new SeqScan(tid, table.getId(), ""); 12 | Filter filter = new Filter(predicate, ss); 13 | filter.open(); 14 | 15 | int resultCount = 0; 16 | while (filter.hasNext()) { 17 | assertNotNull(filter.next()); 18 | resultCount += 1; 19 | } 20 | 21 | filter.close(); 22 | return resultCount; 23 | } 24 | 25 | /** Make test compatible with older version of ant. */ 26 | public static junit.framework.Test suite() { 27 | return new junit.framework.JUnit4TestAdapter(FilterTest.class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/InsertTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import simpledb.*; 6 | 7 | import static org.junit.Assert.*; 8 | import org.junit.Test; 9 | 10 | public class InsertTest extends SimpleDbTestBase { 11 | private void validateInsert(int columns, int sourceRows, int destinationRows) 12 | throws DbException, IOException, TransactionAbortedException { 13 | // Create the two tables 14 | ArrayList> sourceTuples = new ArrayList>(); 15 | HeapFile source = SystemTestUtil.createRandomHeapFile( 16 | columns, sourceRows, null, sourceTuples); 17 | assert sourceTuples.size() == sourceRows; 18 | ArrayList> destinationTuples = new ArrayList>(); 19 | HeapFile destination = SystemTestUtil.createRandomHeapFile( 20 | columns, destinationRows, null, destinationTuples); 21 | assert destinationTuples.size() == destinationRows; 22 | 23 | // Insert source into destination 24 | TransactionId tid = new TransactionId(); 25 | SeqScan ss = new SeqScan(tid, source.getId(), ""); 26 | Insert insOp = new Insert(tid, ss, destination.getId()); 27 | 28 | // Query q = new Query(insOp, tid); 29 | insOp.open(); 30 | boolean hasResult = false; 31 | while (insOp.hasNext()) { 32 | Tuple tup = insOp.next(); 33 | assertFalse(hasResult); 34 | hasResult = true; 35 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, tup.getTupleDesc()); 36 | assertEquals(sourceRows, ((IntField) tup.getField(0)).getValue()); 37 | } 38 | assertTrue(hasResult); 39 | insOp.close(); 40 | 41 | // As part of the same transaction, scan the table 42 | sourceTuples.addAll(destinationTuples); 43 | SystemTestUtil.matchTuples(destination, tid, sourceTuples); 44 | 45 | // As part of a different transaction, scan the table 46 | Database.getBufferPool().transactionComplete(tid); 47 | Database.getBufferPool().flushAllPages(); 48 | SystemTestUtil.matchTuples(destination, sourceTuples); 49 | } 50 | 51 | @Test public void testEmptyToEmpty() 52 | throws IOException, DbException, TransactionAbortedException { 53 | validateInsert(3, 0, 0); 54 | } 55 | 56 | @Test public void testEmptyToOne() 57 | throws IOException, DbException, TransactionAbortedException { 58 | validateInsert(8, 0, 1); 59 | } 60 | 61 | @Test public void testOneToEmpty() 62 | throws IOException, DbException, TransactionAbortedException { 63 | validateInsert(3, 1, 0); 64 | } 65 | 66 | @Test public void testOneToOne() 67 | throws IOException, DbException, TransactionAbortedException { 68 | validateInsert(1, 1, 1); 69 | } 70 | 71 | /** Make test compatible with older version of ant. */ 72 | public static junit.framework.Test suite() { 73 | return new junit.framework.JUnit4TestAdapter(InsertTest.class); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/JoinTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | 7 | import org.junit.Test; 8 | 9 | import simpledb.*; 10 | 11 | public class JoinTest extends SimpleDbTestBase { 12 | private static final int COLUMNS = 2; 13 | public void validateJoin(int table1ColumnValue, int table1Rows, int table2ColumnValue, 14 | int table2Rows) 15 | throws IOException, DbException, TransactionAbortedException { 16 | // Create the two tables 17 | HashMap columnSpecification = new HashMap(); 18 | columnSpecification.put(0, table1ColumnValue); 19 | ArrayList> t1Tuples = new ArrayList>(); 20 | HeapFile table1 = SystemTestUtil.createRandomHeapFile( 21 | COLUMNS, table1Rows, columnSpecification, t1Tuples); 22 | assert t1Tuples.size() == table1Rows; 23 | 24 | columnSpecification.put(0, table2ColumnValue); 25 | ArrayList> t2Tuples = new ArrayList>(); 26 | HeapFile table2 = SystemTestUtil.createRandomHeapFile( 27 | COLUMNS, table2Rows, columnSpecification, t2Tuples); 28 | assert t2Tuples.size() == table2Rows; 29 | 30 | // Generate the expected results 31 | ArrayList> expectedResults = new ArrayList>(); 32 | for (ArrayList t1 : t1Tuples) { 33 | for (ArrayList t2 : t2Tuples) { 34 | // If the columns match, join the tuples 35 | if (t1.get(0).equals(t2.get(0))) { 36 | ArrayList out = new ArrayList(t1); 37 | out.addAll(t2); 38 | expectedResults.add(out); 39 | } 40 | } 41 | } 42 | 43 | // Begin the join 44 | TransactionId tid = new TransactionId(); 45 | SeqScan ss1 = new SeqScan(tid, table1.getId(), ""); 46 | SeqScan ss2 = new SeqScan(tid, table2.getId(), ""); 47 | JoinPredicate p = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 48 | Join joinOp = new Join(p, ss1, ss2); 49 | 50 | // test the join results 51 | SystemTestUtil.matchTuples(joinOp, expectedResults); 52 | 53 | joinOp.close(); 54 | Database.getBufferPool().transactionComplete(tid); 55 | } 56 | 57 | @Test public void testSingleMatch() 58 | throws IOException, DbException, TransactionAbortedException { 59 | validateJoin(1, 1, 1, 1); 60 | } 61 | 62 | @Test public void testNoMatch() 63 | throws IOException, DbException, TransactionAbortedException { 64 | validateJoin(1, 2, 2, 10); 65 | } 66 | 67 | @Test public void testMultipleMatch() 68 | throws IOException, DbException, TransactionAbortedException { 69 | validateJoin(1, 3, 1, 3); 70 | } 71 | 72 | /** Make test compatible with older version of ant. */ 73 | public static junit.framework.Test suite() { 74 | return new junit.framework.JUnit4TestAdapter(JoinTest.class); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/ScanTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import simpledb.systemtest.SystemTestUtil; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.ArrayList; 10 | import java.util.NoSuchElementException; 11 | import java.util.Random; 12 | 13 | import org.junit.Test; 14 | 15 | import simpledb.*; 16 | 17 | /** 18 | * Dumps the contents of a table. 19 | * args[1] is the number of columns. E.g., if it's 5, then ScanTest will end 20 | * up dumping the contents of f4.0.txt. 21 | */ 22 | public class ScanTest extends SimpleDbTestBase { 23 | private final static Random r = new Random(); 24 | 25 | /** Tests the scan operator for a table with the specified dimensions. */ 26 | private void validateScan(int[] columnSizes, int[] rowSizes) 27 | throws IOException, DbException, TransactionAbortedException { 28 | for (int columns : columnSizes) { 29 | for (int rows : rowSizes) { 30 | ArrayList> tuples = new ArrayList>(); 31 | HeapFile f = SystemTestUtil.createRandomHeapFile(columns, rows, null, tuples); 32 | SystemTestUtil.matchTuples(f, tuples); 33 | Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 34 | } 35 | } 36 | } 37 | 38 | /** Scan 1-4 columns. */ 39 | @Test public void testSmall() throws IOException, DbException, TransactionAbortedException { 40 | int[] columnSizes = new int[]{1, 2, 3, 4}; 41 | int[] rowSizes = 42 | new int[]{0, 1, 2, 511, 512, 513, 1023, 1024, 1025, 4096 + r.nextInt(4096)}; 43 | validateScan(columnSizes, rowSizes); 44 | } 45 | 46 | /** Test that rewinding a SeqScan iterator works. */ 47 | @Test public void testRewind() throws IOException, DbException, TransactionAbortedException { 48 | ArrayList> tuples = new ArrayList>(); 49 | HeapFile f = SystemTestUtil.createRandomHeapFile(2, 1000, null, tuples); 50 | 51 | TransactionId tid = new TransactionId(); 52 | SeqScan scan = new SeqScan(tid, f.getId(), "table"); 53 | scan.open(); 54 | for (int i = 0; i < 100; ++i) { 55 | assertTrue(scan.hasNext()); 56 | Tuple t = scan.next(); 57 | assertEquals(tuples.get(i), SystemTestUtil.tupleToList(t)); 58 | } 59 | 60 | scan.rewind(); 61 | for (int i = 0; i < 100; ++i) { 62 | assertTrue(scan.hasNext()); 63 | Tuple t = scan.next(); 64 | assertEquals(tuples.get(i), SystemTestUtil.tupleToList(t)); 65 | } 66 | scan.close(); 67 | Database.getBufferPool().transactionComplete(tid); 68 | } 69 | 70 | /** Verifies that the buffer pool is actually caching data. 71 | * @throws TransactionAbortedException 72 | * @throws DbException */ 73 | @Test public void testCache() throws IOException, DbException, TransactionAbortedException { 74 | /** Counts the number of readPage operations. */ 75 | class InstrumentedHeapFile extends HeapFile { 76 | public InstrumentedHeapFile(File f, TupleDesc td) { 77 | super(f, td); 78 | } 79 | 80 | @Override 81 | public Page readPage(PageId pid) throws NoSuchElementException { 82 | readCount += 1; 83 | return super.readPage(pid); 84 | } 85 | 86 | public int readCount = 0; 87 | } 88 | 89 | // Create the table 90 | final int PAGES = 30; 91 | ArrayList> tuples = new ArrayList>(); 92 | File f = SystemTestUtil.createRandomHeapFileUnopened(1, 992*PAGES, 1000, null, tuples); 93 | TupleDesc td = Utility.getTupleDesc(1); 94 | InstrumentedHeapFile table = new InstrumentedHeapFile(f, td); 95 | Database.getCatalog().addTable(table, SystemTestUtil.getUUID()); 96 | 97 | // Scan the table once 98 | SystemTestUtil.matchTuples(table, tuples); 99 | assertEquals(PAGES, table.readCount); 100 | table.readCount = 0; 101 | 102 | // Scan the table again: all pages should be cached 103 | SystemTestUtil.matchTuples(table, tuples); 104 | assertEquals(0, table.readCount); 105 | } 106 | 107 | /** Make test compatible with older version of ant. */ 108 | public static junit.framework.Test suite() { 109 | return new junit.framework.JUnit4TestAdapter(ScanTest.class); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /test/simpledb/systemtest/SimpleDbTestBase.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import org.junit.Before; 4 | 5 | import simpledb.Database; 6 | 7 | /** 8 | * Base class for all SimpleDb test classes. 9 | * @author nizam 10 | * 11 | */ 12 | public class SimpleDbTestBase { 13 | /** 14 | * Reset the database before each test is run. 15 | */ 16 | @Before public void setUp() throws Exception { 17 | Database.reset(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /turnInLab1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab1submit 3 | git push origin :refs/tags/lab1submit 4 | git add --all . 5 | git commit -a -m 'Lab 1' 6 | git tag -a lab1submit -m 'submit lab 1' 7 | git push origin master --tags 8 | -------------------------------------------------------------------------------- /turnInLab2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab2submit 3 | git push origin :refs/tags/lab2submit 4 | git add --all . 5 | git commit -a -m 'Lab 2' 6 | git tag -a lab2submit -m 'submit lab 2' 7 | git push origin master --tags 8 | -------------------------------------------------------------------------------- /turnInLab3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab3submit 3 | git push origin :refs/tags/lab3submit 4 | git add --all . 5 | git commit -a -m 'Lab 3' 6 | git tag -a lab3submit -m 'submit lab 3' 7 | git push origin master --tags 8 | -------------------------------------------------------------------------------- /turnInLab4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab4submit 3 | git push origin :refs/tags/lab4submit 4 | git add --all . 5 | git commit -a -m 'Lab 4' 6 | git tag -a lab4submit -m 'submit lab 4' 7 | git push origin master --tags 8 | -------------------------------------------------------------------------------- /turnInLab5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab5submit 3 | git push origin :refs/tags/lab5submit 4 | git add --all . 5 | git commit -a -m 'Lab 5' 6 | git tag -a lab5submit -m 'submit lab 5' 7 | git push origin master --tags 8 | -------------------------------------------------------------------------------- /turnInLab6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | git tag -d lab6submit 3 | git push origin :refs/tags/lab6submit 4 | git add --all . 5 | git commit -a -m 'Lab 6' 6 | git tag -a lab6submit -m 'submit lab 6' 7 | git push origin master --tags 8 | --------------------------------------------------------------------------------