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 |
--------------------------------------------------------------------------------