├── .gitignore ├── LICENSE ├── README.md ├── catalog.txt ├── data.dat ├── data.txt ├── doc ├── controlflow.png ├── lab1.md ├── lab2.md ├── lab3-hist.png ├── lab3.md ├── lab4.md ├── lab5.md ├── lab6.md ├── merging_internal.png ├── merging_leaf.png ├── redist_internal.png ├── redist_leaf.png ├── simple_tree.png ├── splitting_internal.png └── splitting_leaf.png ├── document ├── lab1-resolve.md ├── lab2-doc.md ├── lab2-query-lifetime.md ├── lab2-resolve.md ├── lab3-doc.md ├── lab3-resolve.md ├── lab4-resolve.md ├── lab5-resolve.md └── lab6-resolve.md ├── lib ├── README ├── ant-contrib-1.0b3.jar ├── hamcrest-core-1.3.jar ├── jline-0.9.94.jar ├── junit-4.13.1.jar ├── mina-core-2.0.4.jar ├── mina-filter-compression-2.0.4.jar └── zql.jar ├── pdf ├── 01-introduction.pdf ├── 02-advancedsql.pdf ├── 03-storage1.pdf ├── 04-storage2.pdf ├── 05-bufferpool.pdf ├── 06-hashtables.pdf ├── 07-trees1.pdf ├── 08-trees2.pdf ├── 09-indexconcurrency.pdf ├── 10-queryprocessing.pdf ├── 11-sorting.pdf ├── 12-joins.pdf ├── 13-optimization.pdf ├── 14-parallel.pdf ├── 15-embeddedlogic.pdf ├── 16-concurrencycontrol.pdf ├── 17-twophaselocking.pdf ├── 18-timestampordering.pdf ├── 19-multiversioning.pdf ├── 20-logging.pdf ├── 21-recovery.pdf ├── 22-distributedoltp1.pdf ├── 23-distributedoltp2.pdf ├── 24-distributedolap.pdf ├── 26-potpourri.pdf └── picture │ └── ToyDB.png ├── pom.xml ├── src ├── main │ └── java │ │ └── simpledb │ │ ├── Parser.java │ │ ├── ParsingException.java │ │ ├── SimpleDb.java │ │ ├── algorithm │ │ └── Join │ │ │ ├── HashJoin.java │ │ │ ├── JoinStrategy.java │ │ │ ├── NestedLoopJoin.java │ │ │ └── SortMergeJoin.java │ │ ├── common │ │ ├── Catalog.java │ │ ├── Database.java │ │ ├── DbException.java │ │ ├── DeadlockException.java │ │ ├── Debug.java │ │ ├── Permissions.java │ │ ├── TableInfo.java │ │ ├── Type.java │ │ └── Utility.java │ │ ├── execution │ │ ├── Aggregate.java │ │ ├── Aggregator.java │ │ ├── Delete.java │ │ ├── Filter.java │ │ ├── HashEquiJoin.java │ │ ├── IndexOpIterator.java │ │ ├── IndexPredicate.java │ │ ├── Insert.java │ │ ├── IntegerAggregator.java │ │ ├── Join.java │ │ ├── JoinPredicate.java │ │ ├── OpIterator.java │ │ ├── Operator.java │ │ ├── OrderBy.java │ │ ├── PlanCache.java │ │ ├── Predicate.java │ │ ├── Project.java │ │ ├── Query.java │ │ ├── SeqScan.java │ │ └── StringAggregator.java │ │ ├── index │ │ ├── BTreeChecker.java │ │ ├── BTreeEntry.java │ │ ├── BTreeFile.java │ │ ├── BTreeFileEncoder.java │ │ ├── BTreeHeaderPage.java │ │ ├── BTreeInternalPage.java │ │ ├── BTreeLeafPage.java │ │ ├── BTreePage.java │ │ ├── BTreePageId.java │ │ ├── BTreeRootPtrPage.java │ │ ├── BTreeScan.java │ │ └── BTreeUtility.java │ │ ├── optimizer │ │ ├── CostCard.java │ │ ├── Histogram.java │ │ ├── IntHistogram.java │ │ ├── JoinOptimizer.java │ │ ├── LogicalFilterNode.java │ │ ├── LogicalJoinNode.java │ │ ├── LogicalPlan.java │ │ ├── LogicalScanNode.java │ │ ├── LogicalSelectListNode.java │ │ ├── LogicalSubplanJoinNode.java │ │ ├── OperatorCardinality.java │ │ ├── QueryPlanVisualizer.java │ │ ├── StringHistogram.java │ │ └── TableStats.java │ │ ├── storage │ │ ├── AbstractDbFileIterator.java │ │ ├── BufferPool.java │ │ ├── DbFile.java │ │ ├── DbFileIterator.java │ │ ├── Field.java │ │ ├── HeapFile.java │ │ ├── HeapFileEncoder.java │ │ ├── HeapPage.java │ │ ├── HeapPageId.java │ │ ├── IntField.java │ │ ├── LogFile.java │ │ ├── Page.java │ │ ├── PageId.java │ │ ├── RecordId.java │ │ ├── StringField.java │ │ ├── Tuple.java │ │ ├── TupleDesc.java │ │ └── TupleIterator.java │ │ ├── transaction │ │ ├── Lock.java │ │ ├── LockManager.java │ │ ├── Transaction.java │ │ ├── TransactionAbortedException.java │ │ └── TransactionId.java │ │ └── util │ │ ├── HeapFileIterator.java │ │ ├── IteratorWrapper.java │ │ ├── LruCache.java │ │ └── PageCachePool.java └── test │ └── simpledb │ ├── AggregateTest.java │ ├── BTreeDeadlockTest.java │ ├── BTreeFileDeleteTest.java │ ├── BTreeFileInsertTest.java │ ├── BTreeFileReadTest.java │ ├── BTreeHeaderPageTest.java │ ├── BTreeInternalPageTest.java │ ├── BTreeLeafPageTest.java │ ├── BTreeNextKeyLockingTest.java │ ├── BTreePageIdTest.java │ ├── BTreeRootPtrPageTest.java │ ├── BufferPoolWriteTest.java │ ├── CatalogTest.java │ ├── DeadlockTest.java │ ├── FilterTest.java │ ├── HeapFileReadTest.java │ ├── HeapFileWriteTest.java │ ├── HeapPageIdTest.java │ ├── HeapPageReadTest.java │ ├── HeapPageWriteTest.java │ ├── InsertTest.java │ ├── IntHistogramTest.java │ ├── IntegerAggregatorTest.java │ ├── JoinOptimizerTest.java │ ├── JoinPredicateTest.java │ ├── JoinTest.java │ ├── LockingTest.java │ ├── PredicateTest.java │ ├── RecordIdTest.java │ ├── StringAggregatorTest.java │ ├── TableStatsTest.java │ ├── TestUtil.java │ ├── TransactionTest.java │ ├── TupleDescTest.java │ ├── TupleTest.java │ └── systemtest │ ├── AbortEvictionTest.java │ ├── AggregateTest.java │ ├── BTreeFileDeleteTest.java │ ├── BTreeFileInsertTest.java │ ├── BTreeScanTest.java │ ├── BTreeTest.java │ ├── DeleteTest.java │ ├── EvictionTest.java │ ├── FilterBase.java │ ├── FilterTest.java │ ├── InsertTest.java │ ├── JoinTest.java │ ├── LogTest.java │ ├── QueryTest.java │ ├── ScanTest.java │ ├── SimpleDbTestBase.java │ ├── SystemTestUtil.java │ └── TransactionTest.java └── tools ├── check_format.sh └── codestyle ├── HEADER └── formatter.xml /.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | bin/ 3 | out/ 4 | *.db 5 | *.iml 6 | .idea/ 7 | .DS_Store 8 | *.zip 9 | dist/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 MIT-DB-Class 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 总览 2 | 3 | ![img](https://gitee.com/zisuu/mypicture/raw/master/20200103180012189.png) 4 | 5 | 在开始 simpledb 旅途之前, 我们先从整体上来看看 6 | 7 | SimpleDb 是一个 DBMS 数据库管理系统, 包含存储, 算子, 优化, 事务, 索引 等, 全方位介绍了如何从0实现一个 DBMS, 可以说, 这门课是学习 TIDB 等其他分布式数据库的前提. 8 | 9 | 项目文档: 10 | 11 | - [lab1 - 存储模型](document/lab1-resolve.md) 12 | 13 | - [lab2 - 常见算子和 volcano 执行模型](document/lab2-resolve.md) 14 | 15 | - [lab3 - 优化器](document/lab3-resolve.md) 16 | 17 | - [lab4 - 基于 2pl 的事务](document/lab4-resolve.md) 18 | 19 | - [lab5 - b+ 树索引](document/lab5-resolve.md) 20 | 21 | - [lab6 - 崩溃恢复与回滚](document/lab6-resolve.md) 22 | 23 | ## lab1 - Storage 24 | ![image-20211003151458924](https://gitee.com/zisuu/mypicture/raw/master/597db65e95f3031f3ed39f8381d7bcbf.png) 25 | 26 | 27 | lab1 主要涉及存储 -- 也即和各种 file, page, bufferPool 等打交道 28 | 29 | - TupleDesc: td 描述了一个表每一列的元数据, 也即每个列的类型等等 30 | - Tuple: 代表了一行的数据 31 | - Page: 代表一个表的某个 page, page 由 header 和 body 组成, header 是一个 bitmap, 记录了body 中哪个位置是存在数据的. body 中存储了一个个 Tuple 32 | - DbFile: SimpleDb 中, 一个 Table 用一个 file 进行存储, 每个 file 包含了若干个 page 33 | - BufferPool: SimpleDb 的缓存组件, 可以搭配 Lru 缓存, 效果更佳. 是整个系统最核心的组件, 任何地方访问一个 page 都需要通过 bufferPool.getPage() 方法 34 | - CataLog: SimpleDb 等全局目录, 包含了tableid 和 table 的映射关系等 35 | 36 | ## lab2 - Operators & Volcano 37 | 38 | lab2 主要涉及算子的开发: 也即各种 Operator, 如 seqScan, join, aggregation 等 39 | 40 | 需要注意的是, SimpleDb 采用了的 process model 是 volcano model, 每个算子都实现了相同的接口 --- OpIterator 41 | 42 | - SeqScan: 顺序扫描表的算子, 需要做一些缓存 43 | - Join + JoinPredicate: join 算子, 可以自己实现 简单的 nestedLoopJoin, 或者 sortMergeJoin 44 | - Filter + Predicate: filter 算子, 主要用于 where 后面的条件判断 45 | - Aggregate: aggregation 算子, 主要用于 sum() 等聚合函数 46 | - Insert / Delete: 插入/删除算子 47 | 48 | 关于 Volcano model, 举个例子, 在 lab2 中会更详细的介绍![img](https://gitee.com/zisuu/mypicture/raw/master/2282357-20210228200010429-1462288556.png) 49 | 50 | ## lab3 -- Query Optimization 51 | 52 | 这个实验主要介绍了如何简单的进行数据估算和 join 优化 53 | 54 | - 利用直方图进行谓词预估统计 55 | - 利用 left-deep-tree 和动态规划算法进行 Join Optimizer 56 | - 代码量较少 57 | 58 | 流程图如下: 59 | 60 | ![img](https://img-blog.csdnimg.cn/20191220224026447.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2hqdzE5OTY2Ng==,size_16,color_FFFFFF,t_70) 61 | 62 | ## lab4 -- Transaction 63 | 64 | 实验四要求我们实现基于 2pl 协议的事务, 先来说一下在 simpleDB 中是如何实现事务的: 65 | 66 | ![image-20211213163243849](https://gitee.com/zisuu/mypicture/raw/master/image-20211213163243849.png) 67 | 68 | 在SimpleDB中,每个事务都会有一个Transaction对象,我们用TransactionId来唯一标识一个事务,TransactionId在Transaction对象创建时自动获取。事务开始前,会创建一个Transaction对象,trasactionId 会被传入到 sql 执行树的每一个 operator 算子中,加锁时根据加锁页面、锁的类型、加锁的事务id去进行加锁。 69 | 70 | 比如, 底层的 A, B seqScan 算子, 就会给对应的 page 加读锁. 71 | 72 | 我们知道, page 是通过 bufferPool.getPage() 来统一获取的, 因此, 加锁的逻辑就在 bufferPool.getPage() 中 73 | 74 | 具体的方法就是实现一个 lockManager, lockManager 包含每个 page 和其持有其锁的事务的队列 75 | 76 | 当事务完成时,调用transactionComplete去完成最后的处理。transactionComplete会根据成功还是失败去分别处理,如果成功,会将事务id对应的脏页写到磁盘中,如果失败,会将事务id对应的脏页淘汰出bufferpool并从磁盘中获取原来的数据页。脏页处理完成后,会释放事务id在所有数据页中加的锁。 77 | 78 | - 需要实现一个 LockManager, 跟踪每一个 transaction 持有的锁, 并进行锁管理. 79 | - 需要实现 LifeTime lock, 也即有限等待策略 80 | - 需要实现 DeadLock detect, 可以采用超时等待, 也可以通过依赖图进行检查 81 | 82 | ## lab5 -- B+ tree 83 | ![img](https:////upload-images.jianshu.io/upload_images/7862980-42f0acde88d3c0cb.png?imageMogr2/auto-orient/strip|imageView2/2/w/825/format/webp) 84 | 85 | lab5主要是实现B+树索引,主要有查询、插入、删除等功能 86 | 87 | - 查询主要根据B+树的特性去递归查找即可 88 | - 插入要考虑节点的分裂(节点tuples满的时候) 89 | - 删除要考虑节点内元素的重新分配(当一个页面比较空,相邻页面比较满的时候),兄弟节点的合并(当相邻两个页面的元素都比较空的时候) 90 | 91 | ## lab6 -- log & rollback & recover 92 | 93 | lab6 主要是实现一个 redo log & undo log 日志系统, 使得 simpledb 支持日志回滚和崩溃恢复 94 | 95 | 96 | ## 总结 97 | 98 | 总的来说, 实验难度不大, 但是可以让我们快速入门数据库领域, 可以说是顶级的数据库课程了. 99 | -------------------------------------------------------------------------------- /catalog.txt: -------------------------------------------------------------------------------- 1 | data (f1 int, f2 int) -------------------------------------------------------------------------------- /data.dat: -------------------------------------------------------------------------------- 1 |  2 | (2 -------------------------------------------------------------------------------- /data.txt: -------------------------------------------------------------------------------- 1 | 1,10 2 | 2,20 3 | 3,30 4 | 4,40 5 | 5,50 6 | 5,50 -------------------------------------------------------------------------------- /doc/controlflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/controlflow.png -------------------------------------------------------------------------------- /doc/lab3-hist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/lab3-hist.png -------------------------------------------------------------------------------- /doc/merging_internal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/merging_internal.png -------------------------------------------------------------------------------- /doc/merging_leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/merging_leaf.png -------------------------------------------------------------------------------- /doc/redist_internal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/redist_internal.png -------------------------------------------------------------------------------- /doc/redist_leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/redist_leaf.png -------------------------------------------------------------------------------- /doc/simple_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/simple_tree.png -------------------------------------------------------------------------------- /doc/splitting_internal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/splitting_internal.png -------------------------------------------------------------------------------- /doc/splitting_leaf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/doc/splitting_leaf.png -------------------------------------------------------------------------------- /document/lab2-doc.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/document/lab2-doc.md -------------------------------------------------------------------------------- /document/lab3-doc.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/document/lab3-doc.md -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | junit-4.13.1.jar 2 | * https://search.maven.org/search?q=g:junit%20AND%20a:junit 3 | * CPL (free for all use) 4 | 5 | zql.jar 6 | * http://www.gibello.com/code/zql/ 7 | * Free for non-commercial use 8 | 9 | JLine 10 | * http://jline.sourceforge.net/ 11 | * BSD (free for all use) 12 | 13 | mina-core-2.0.4.jar 14 | mina-filter-compression-2.0.4.jar 15 | * http://mina.apache.org/ 16 | * Apache License v2.0 (free for all use) 17 | 18 | slf4j-api-1.6.1.jar 19 | slf4j-log4j12-1.6.1.jar 20 | * http://www.slf4j.org/license.html 21 | * MIT license (free for all use) 22 | 23 | jzlib-1.0.7.jar 24 | * http://www.jcraft.com/jzlib/ 25 | * BSD (free for all use) 26 | 27 | javassist-3.27.0-GA.jar 28 | * http://www.javassist.org/ 29 | * MPL v1.1, LGPL and Apache License 30 | 31 | ant-contrib-1.0b3.jar 32 | * http://ant-contrib.sourceforge.net/ 33 | * Apache Software License 34 | 35 | log4j-1.2.17.jar 36 | * logging.apache.org/log4j/1.2/ 37 | * Apache Software license 2.0 38 | 39 | hamcrest-core-1.3.jar 40 | * https://search.maven.org/artifact/org.hamcrest/hamcrest-core/1.3/jar 41 | * BSD License 42 | 43 | -------------------------------------------------------------------------------- /lib/ant-contrib-1.0b3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/ant-contrib-1.0b3.jar -------------------------------------------------------------------------------- /lib/hamcrest-core-1.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/hamcrest-core-1.3.jar -------------------------------------------------------------------------------- /lib/jline-0.9.94.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/jline-0.9.94.jar -------------------------------------------------------------------------------- /lib/junit-4.13.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/junit-4.13.1.jar -------------------------------------------------------------------------------- /lib/mina-core-2.0.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/mina-core-2.0.4.jar -------------------------------------------------------------------------------- /lib/mina-filter-compression-2.0.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/mina-filter-compression-2.0.4.jar -------------------------------------------------------------------------------- /lib/zql.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/lib/zql.jar -------------------------------------------------------------------------------- /pdf/01-introduction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/01-introduction.pdf -------------------------------------------------------------------------------- /pdf/02-advancedsql.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/02-advancedsql.pdf -------------------------------------------------------------------------------- /pdf/03-storage1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/03-storage1.pdf -------------------------------------------------------------------------------- /pdf/04-storage2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/04-storage2.pdf -------------------------------------------------------------------------------- /pdf/05-bufferpool.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/05-bufferpool.pdf -------------------------------------------------------------------------------- /pdf/06-hashtables.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/06-hashtables.pdf -------------------------------------------------------------------------------- /pdf/07-trees1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/07-trees1.pdf -------------------------------------------------------------------------------- /pdf/08-trees2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/08-trees2.pdf -------------------------------------------------------------------------------- /pdf/09-indexconcurrency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/09-indexconcurrency.pdf -------------------------------------------------------------------------------- /pdf/10-queryprocessing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/10-queryprocessing.pdf -------------------------------------------------------------------------------- /pdf/11-sorting.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/11-sorting.pdf -------------------------------------------------------------------------------- /pdf/12-joins.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/12-joins.pdf -------------------------------------------------------------------------------- /pdf/13-optimization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/13-optimization.pdf -------------------------------------------------------------------------------- /pdf/14-parallel.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/14-parallel.pdf -------------------------------------------------------------------------------- /pdf/15-embeddedlogic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/15-embeddedlogic.pdf -------------------------------------------------------------------------------- /pdf/16-concurrencycontrol.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/16-concurrencycontrol.pdf -------------------------------------------------------------------------------- /pdf/17-twophaselocking.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/17-twophaselocking.pdf -------------------------------------------------------------------------------- /pdf/18-timestampordering.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/18-timestampordering.pdf -------------------------------------------------------------------------------- /pdf/19-multiversioning.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/19-multiversioning.pdf -------------------------------------------------------------------------------- /pdf/20-logging.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/20-logging.pdf -------------------------------------------------------------------------------- /pdf/21-recovery.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/21-recovery.pdf -------------------------------------------------------------------------------- /pdf/22-distributedoltp1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/22-distributedoltp1.pdf -------------------------------------------------------------------------------- /pdf/23-distributedoltp2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/23-distributedoltp2.pdf -------------------------------------------------------------------------------- /pdf/24-distributedolap.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/24-distributedolap.pdf -------------------------------------------------------------------------------- /pdf/26-potpourri.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/26-potpourri.pdf -------------------------------------------------------------------------------- /pdf/picture/ToyDB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreatorsStack/CreatorDB/eae98ff76618354004bc6c7c12a578814ec0392b/pdf/picture/ToyDB.png -------------------------------------------------------------------------------- /src/main/java/simpledb/ParsingException.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import java.lang.Exception; 4 | 5 | public class ParsingException extends Exception { 6 | public ParsingException(String string) { 7 | super(string); 8 | } 9 | 10 | public ParsingException(Exception e) { 11 | super(e); 12 | } 13 | 14 | /** 15 | * 16 | */ 17 | private static final long serialVersionUID = 1L; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/simpledb/algorithm/Join/HashJoin.java: -------------------------------------------------------------------------------- 1 | package simpledb.algorithm.Join; 2 | 3 | import simpledb.execution.JoinPredicate; 4 | import simpledb.execution.OpIterator; 5 | import simpledb.storage.TupleDesc; 6 | import simpledb.storage.TupleIterator; 7 | 8 | // An impl of simple hashJoin 9 | public class HashJoin extends JoinStrategy { 10 | 11 | public HashJoin(final OpIterator child1, final OpIterator child2, final TupleDesc td, 12 | final JoinPredicate joinPredicate) { 13 | super(child1, child2, td, joinPredicate); 14 | } 15 | 16 | @Override 17 | public TupleIterator doJoin() { 18 | return null; 19 | } 20 | 21 | @Override 22 | public void close() { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/simpledb/algorithm/Join/JoinStrategy.java: -------------------------------------------------------------------------------- 1 | package simpledb.algorithm.Join; 2 | 3 | import simpledb.execution.JoinPredicate; 4 | import simpledb.execution.OpIterator; 5 | import simpledb.storage.Tuple; 6 | import simpledb.storage.TupleDesc; 7 | import simpledb.storage.TupleIterator; 8 | 9 | import java.util.Arrays; 10 | 11 | public abstract class JoinStrategy { 12 | 13 | protected final OpIterator child1; 14 | protected final OpIterator child2; 15 | protected final TupleDesc td; 16 | protected final JoinPredicate joinPredicate; 17 | 18 | public JoinStrategy(final OpIterator child1, final OpIterator child2, final TupleDesc td, 19 | final JoinPredicate joinPredicate) { 20 | this.child1 = child1; 21 | this.child2 = child2; 22 | this.td = td; 23 | this.joinPredicate = joinPredicate; 24 | } 25 | 26 | protected Tuple mergeTuple(final Tuple tuple1, final Tuple tuple2, final TupleDesc td) { 27 | final Tuple tuple = new Tuple(td); 28 | int len1 = tuple1.getTupleDesc().numFields(); 29 | for (int i = 0; i < len1; i++) { 30 | tuple.setField(i, tuple1.getField(i)); 31 | } 32 | for (int i = 0; i < tuple2.getTupleDesc().numFields(); i++) { 33 | tuple.setField(i + len1, tuple2.getField(i)); 34 | } 35 | return tuple; 36 | } 37 | 38 | protected int fetchTuples(final OpIterator child, final Tuple[] tuples) throws Exception { 39 | int i = 0; 40 | Arrays.fill(tuples, null); 41 | while (child.hasNext() && i < tuples.length) { 42 | final Tuple next = child.next(); 43 | if (next != null) { 44 | tuples[i++] = next; 45 | } 46 | } 47 | return i; 48 | } 49 | 50 | // Join child1 and child2, return a tuple iterator result 51 | public abstract TupleIterator doJoin(); 52 | 53 | public abstract void close(); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/simpledb/algorithm/Join/NestedLoopJoin.java: -------------------------------------------------------------------------------- 1 | package simpledb.algorithm.Join; 2 | 3 | import simpledb.execution.JoinPredicate; 4 | import simpledb.execution.OpIterator; 5 | import simpledb.storage.Tuple; 6 | import simpledb.storage.TupleDesc; 7 | import simpledb.storage.TupleIterator; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | // An impl for nestedLoopJoin 13 | public class NestedLoopJoin extends JoinStrategy { 14 | 15 | public NestedLoopJoin(final OpIterator child1, final OpIterator child2, final TupleDesc td, 16 | final JoinPredicate joinPredicate) { 17 | super(child1, child2, td, joinPredicate); 18 | } 19 | 20 | @Override 21 | public TupleIterator doJoin() { 22 | final List tuples = new ArrayList<>(); 23 | try { 24 | child1.rewind(); 25 | while (child1.hasNext()) { 26 | final Tuple lTuple = child1.next(); 27 | child2.rewind(); 28 | while (child2.hasNext()) { 29 | final Tuple rTuple = child2.next(); 30 | if (this.joinPredicate.filter(lTuple, rTuple)) { 31 | tuples.add(mergeTuple(lTuple, rTuple, this.td)); 32 | } 33 | } 34 | } 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | System.out.println("Error happen when nested loop join"); 38 | } 39 | return new TupleIterator(this.td, tuples); 40 | } 41 | 42 | @Override 43 | public void close() { 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/simpledb/common/Database.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 2 | 3 | import simpledb.storage.BufferPool; 4 | import simpledb.storage.LogFile; 5 | 6 | import java.io.*; 7 | import java.util.concurrent.atomic.AtomicReference; 8 | 9 | /** 10 | * Database is a class that initializes several static variables used by the 11 | * database system (the catalog, the buffer pool, and the log files, in 12 | * particular.) 13 | *

14 | * Provides a set of methods that can be used to access these variables from 15 | * anywhere. 16 | * 17 | * @Threadsafe 18 | */ 19 | public class Database { 20 | private static final AtomicReference _instance = new AtomicReference<>(new Database()); 21 | private final Catalog _catalog; 22 | private final BufferPool _bufferpool; 23 | 24 | private final static String LOGFILENAME = "log"; 25 | private final LogFile _logfile; 26 | 27 | private Database() { 28 | _catalog = new Catalog(); 29 | _bufferpool = new BufferPool(BufferPool.DEFAULT_PAGES); 30 | LogFile tmp = null; 31 | try { 32 | tmp = new LogFile(new File(LOGFILENAME)); 33 | } catch (IOException e) { 34 | e.printStackTrace(); 35 | System.exit(1); 36 | } 37 | _logfile = tmp; 38 | // startControllerThread(); 39 | } 40 | 41 | /** Return the log file of the static Database instance */ 42 | public static LogFile getLogFile() { 43 | return _instance.get()._logfile; 44 | } 45 | 46 | /** Return the buffer pool of the static Database instance */ 47 | public static BufferPool getBufferPool() { 48 | return _instance.get()._bufferpool; 49 | } 50 | 51 | /** Return the catalog of the static Database instance */ 52 | public static Catalog getCatalog() { 53 | return _instance.get()._catalog; 54 | } 55 | 56 | /** 57 | * Method used for testing -- create a new instance of the buffer pool and 58 | * return it 59 | */ 60 | public static BufferPool resetBufferPool(int pages) { 61 | java.lang.reflect.Field bufferPoolF = null; 62 | try { 63 | bufferPoolF = Database.class.getDeclaredField("_bufferpool"); 64 | bufferPoolF.setAccessible(true); 65 | bufferPoolF.set(_instance.get(), new BufferPool(pages)); 66 | } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException | SecurityException e) { 67 | e.printStackTrace(); 68 | } 69 | // _instance._bufferpool = new BufferPool(pages); 70 | return _instance.get()._bufferpool; 71 | } 72 | 73 | // reset the database, used for unit tests only. 74 | public static void reset() { 75 | _instance.set(new Database()); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/simpledb/common/DbException.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 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/main/java/simpledb/common/DeadlockException.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 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/main/java/simpledb/common/Debug.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 2 | 3 | /** 4 | * Debug is a utility class that wraps println statements and allows 5 | * more or less command line output to be turned on. 6 | *

7 | * Change the value of the DEBUG_LEVEL constant using a system property: 8 | * simpledb.common.Debug. For example, on the command line, use -Dsimpledb.Debug=x, 9 | * or simply -Dsimpledb.Debug to enable it at level 0. 10 | * The log(level, message, ...) method will print to standard output if the 11 | * level number is less than or equal to the currently set DEBUG_LEVEL. 12 | */ 13 | 14 | public class Debug { 15 | private static final int DEBUG_LEVEL; 16 | static { 17 | String debug = System.getProperty("simpledb.common.Debug"); 18 | if (debug == null) { 19 | // No system property = disabled 20 | DEBUG_LEVEL = -1; 21 | } else if (debug.length() == 0) { 22 | // Empty property = level 0 23 | DEBUG_LEVEL = 0; 24 | } else { 25 | DEBUG_LEVEL = Integer.parseInt(debug); 26 | } 27 | } 28 | 29 | private static final int DEFAULT_LEVEL = 0; 30 | 31 | /** Log message if the log level >= level. Uses printf. */ 32 | public static void log(int level, String message, Object... args) { 33 | if (isEnabled(level)) { 34 | System.out.printf(message, args); 35 | System.out.println(); 36 | } 37 | } 38 | 39 | /** @return true if level is being logged. */ 40 | public static boolean isEnabled(int level) { 41 | return level <= DEBUG_LEVEL; 42 | } 43 | 44 | /** @return true if the default level is being logged. */ 45 | public static boolean isEnabled() { 46 | return isEnabled(DEFAULT_LEVEL); 47 | } 48 | 49 | /** Logs message at the default log level. */ 50 | public static void log(String message, Object... args) { 51 | log(DEFAULT_LEVEL, message, args); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/simpledb/common/Permissions.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 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 enum Permissions { 9 | READ_ONLY, READ_WRITE 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/simpledb/common/TableInfo.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 2 | 3 | import simpledb.storage.DbFile; 4 | 5 | public class TableInfo { 6 | 7 | private int tableId; 8 | private String tableName; 9 | private DbFile dbFile; 10 | private String primaryKeyName; 11 | 12 | public TableInfo(final int tableId, final String tableName, final DbFile dbFile, final String primaryKeyName) { 13 | this.tableId = tableId; 14 | this.tableName = tableName; 15 | this.dbFile = dbFile; 16 | this.primaryKeyName = primaryKeyName; 17 | } 18 | 19 | public int getTableId() { 20 | return tableId; 21 | } 22 | 23 | public void setTableId(final int tableId) { 24 | this.tableId = tableId; 25 | } 26 | 27 | public String getTableName() { 28 | return tableName; 29 | } 30 | 31 | public void setTableName(final String tableName) { 32 | this.tableName = tableName; 33 | } 34 | 35 | public DbFile getDbFile() { 36 | return dbFile; 37 | } 38 | 39 | public void setDbFile(final DbFile dbFile) { 40 | this.dbFile = dbFile; 41 | } 42 | 43 | public String getPrimaryKeyName() { 44 | return primaryKeyName; 45 | } 46 | 47 | public void setPrimaryKeyName(final String primaryKeyName) { 48 | this.primaryKeyName = primaryKeyName; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/simpledb/common/Type.java: -------------------------------------------------------------------------------- 1 | package simpledb.common; 2 | 3 | import simpledb.storage.StringField; 4 | import simpledb.storage.Field; 5 | import simpledb.storage.IntField; 6 | 7 | import java.text.ParseException; 8 | import java.io.*; 9 | 10 | /** 11 | * Class representing a type in SimpleDB. 12 | * Types are static objects defined by this class; hence, the Type 13 | * constructor is private. 14 | */ 15 | public enum Type implements Serializable { 16 | INT_TYPE() { 17 | @Override 18 | public int getLen() { 19 | return 4; 20 | } 21 | 22 | @Override 23 | public Field parse(DataInputStream dis) throws ParseException { 24 | try { 25 | return new IntField(dis.readInt()); 26 | } catch (IOException e) { 27 | throw new ParseException("couldn't parse", 0); 28 | } 29 | } 30 | 31 | }, 32 | STRING_TYPE() { 33 | @Override 34 | public int getLen() { 35 | return STRING_LEN + 4; 36 | } 37 | 38 | @Override 39 | public Field parse(DataInputStream dis) throws ParseException { 40 | try { 41 | int strLen = dis.readInt(); 42 | byte[] bs = new byte[strLen]; 43 | dis.read(bs); 44 | dis.skipBytes(STRING_LEN - strLen); 45 | return new StringField(new String(bs), STRING_LEN); 46 | } catch (IOException e) { 47 | throw new ParseException("couldn't parse", 0); 48 | } 49 | } 50 | }; 51 | 52 | public static final int STRING_LEN = 128; 53 | 54 | /** 55 | * @return the number of bytes required to store a field of this type. 56 | */ 57 | public abstract int getLen(); 58 | 59 | /** 60 | * @return a Field object of the same type as this object that has contents 61 | * read from the specified DataInputStream. 62 | * @param dis The input stream to read from 63 | * @throws ParseException if the data read from the input stream is not 64 | * of the appropriate type. 65 | */ 66 | public abstract Field parse(DataInputStream dis) throws ParseException; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Aggregator.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.storage.Tuple; 4 | import simpledb.storage.TupleIterator; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * The common interface for any class that can compute an aggregate over a 10 | * list of Tuples. 11 | */ 12 | public interface Aggregator extends Serializable { 13 | int NO_GROUPING = -1; 14 | 15 | /** 16 | * SUM_COUNT and SC_AVG will 17 | * only be used in lab7, you are not required 18 | * to implement them until then. 19 | * */ 20 | enum Op implements Serializable { 21 | MIN, MAX, SUM, AVG, COUNT, 22 | /** 23 | * SUM_COUNT: compute sum and count simultaneously, will be 24 | * needed to compute distributed avg in lab7. 25 | * */ 26 | SUM_COUNT, 27 | /** 28 | * SC_AVG: compute the avg of a set of SUM_COUNT tuples, 29 | * will be used to compute distributed avg in lab7. 30 | * */ 31 | SC_AVG; 32 | 33 | /** 34 | * Interface to access operations by a string containing an integer 35 | * index for command-line convenience. 36 | * 37 | * @param s a string containing a valid integer Op index 38 | */ 39 | public static Op getOp(String s) { 40 | return getOp(Integer.parseInt(s)); 41 | } 42 | 43 | /** 44 | * Interface to access operations by integer value for command-line 45 | * convenience. 46 | * 47 | * @param i a valid integer Op index 48 | */ 49 | public static Op getOp(int i) { 50 | return values()[i]; 51 | } 52 | 53 | public String toString() { 54 | if (this == MIN) 55 | return "min"; 56 | if (this == MAX) 57 | return "max"; 58 | if (this == SUM) 59 | return "sum"; 60 | if (this == SUM_COUNT) 61 | return "sum_count"; 62 | if (this == AVG) 63 | return "avg"; 64 | if (this == COUNT) 65 | return "count"; 66 | if (this == SC_AVG) 67 | return "sc_avg"; 68 | throw new IllegalStateException("impossible to reach here"); 69 | } 70 | } 71 | 72 | /** 73 | * Merge a new tuple into the aggregate for a distinct group value; 74 | * creates a new group aggregate result if the group value has not yet 75 | * been encountered. 76 | * 77 | * @param tup the Tuple containing an aggregate field and a group-by field 78 | */ 79 | void mergeTupleIntoGroup(Tuple tup); 80 | 81 | /** 82 | * Create a OpIterator over group aggregate results. 83 | * @see TupleIterator for a possible helper 84 | */ 85 | OpIterator iterator(); 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Delete.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.common.Database; 4 | import simpledb.common.DbException; 5 | import simpledb.common.Type; 6 | import simpledb.storage.BufferPool; 7 | import simpledb.storage.IntField; 8 | import simpledb.storage.Tuple; 9 | import simpledb.storage.TupleDesc; 10 | import simpledb.transaction.TransactionAbortedException; 11 | import simpledb.transaction.TransactionId; 12 | 13 | /** 14 | * The delete operator. Delete reads tuples from its child operator and removes 15 | * them from the table they belong to. 16 | */ 17 | public class Delete extends Operator { 18 | 19 | private static final long serialVersionUID = 1L; 20 | 21 | private TransactionId tid; 22 | private OpIterator child; 23 | private int tableId; 24 | private TupleDesc td; 25 | private boolean isFetched; 26 | 27 | /** 28 | * Constructor specifying the transaction that this delete belongs to as 29 | * well as the child to read from. 30 | * 31 | * @param t 32 | * The transaction this delete runs in 33 | * @param child 34 | * The child operator from which to read tuples for deletion 35 | */ 36 | public Delete(TransactionId t, OpIterator child) { 37 | // some code goes here 38 | this.tid = t; 39 | this.child = child; 40 | this.tableId = tableId; 41 | final Type[] types = new Type[] { Type.INT_TYPE }; 42 | this.td = new TupleDesc(types); 43 | } 44 | 45 | public TupleDesc getTupleDesc() { 46 | // some code goes here 47 | return this.td; 48 | } 49 | 50 | public void open() throws DbException, TransactionAbortedException { 51 | // some code goes here 52 | this.child.open(); 53 | super.open(); 54 | isFetched = false; 55 | } 56 | 57 | public void close() { 58 | // some code goes here 59 | this.child.close(); 60 | super.close(); 61 | } 62 | 63 | public void rewind() throws DbException, TransactionAbortedException { 64 | // some code goes here 65 | close(); 66 | open(); 67 | } 68 | 69 | /** 70 | * Deletes tuples as they are read from the child operator. Deletes are 71 | * processed via the buffer pool (which can be accessed via the 72 | * Database.getBufferPool() method. 73 | * 74 | * @return A 1-field tuple containing the number of deleted records. 75 | * @see Database#getBufferPool 76 | * @see BufferPool#deleteTuple 77 | */ 78 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 79 | int cnt = 0; 80 | while (this.child.hasNext()) { 81 | final Tuple next = this.child.next(); 82 | try { 83 | Database.getBufferPool().deleteTuple(this.tid, next); 84 | } catch (Exception e) { 85 | e.printStackTrace(); 86 | System.out.println("Error happen when delete tuple:" + e.getMessage()); 87 | } 88 | cnt++; 89 | } 90 | if (cnt == 0 && isFetched) { 91 | return null; 92 | } 93 | isFetched = true; 94 | final Tuple result = new Tuple(this.td); 95 | result.setField(0, new IntField(cnt)); 96 | return result; 97 | } 98 | 99 | @Override 100 | public OpIterator[] getChildren() { 101 | // some code goes here 102 | return new OpIterator[] { this.child }; 103 | } 104 | 105 | @Override 106 | public void setChildren(OpIterator[] children) { 107 | // some code goes here 108 | if (children.length > 0) { 109 | this.child = children[0]; 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Filter.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.DbException; 5 | import simpledb.storage.Tuple; 6 | import simpledb.storage.TupleDesc; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * Filter is an operator that implements a relational select. 12 | */ 13 | public class Filter extends Operator { 14 | 15 | private static final long serialVersionUID = 1L; 16 | 17 | private Predicate predicate; 18 | private OpIterator child; 19 | private TupleDesc tupleDesc; 20 | 21 | /** 22 | * Constructor accepts a predicate to apply and a child operator to read 23 | * tuples to filter from. 24 | * 25 | * @param p 26 | * The predicate to filter tuples with 27 | * @param child 28 | * The child operator 29 | */ 30 | public Filter(Predicate p, OpIterator child) { 31 | // some code goes here 32 | this.child = child; 33 | this.predicate = p; 34 | this.tupleDesc = child.getTupleDesc(); 35 | } 36 | 37 | public Predicate getPredicate() { 38 | // some code goes here 39 | return this.predicate; 40 | } 41 | 42 | public TupleDesc getTupleDesc() { 43 | // some code goes here 44 | return this.tupleDesc; 45 | } 46 | 47 | public void open() throws DbException, NoSuchElementException, TransactionAbortedException { 48 | // some code goes here 49 | child.open(); 50 | super.open(); 51 | } 52 | 53 | public void close() { 54 | // some code goes here 55 | child.close(); 56 | super.close(); 57 | } 58 | 59 | public void rewind() throws DbException, TransactionAbortedException { 60 | // some code goes here 61 | child.rewind(); 62 | } 63 | 64 | /** 65 | * AbstractDbIterator.readNext implementation. Iterates over tuples from the 66 | * child operator, applying the predicate to them and returning those that 67 | * pass the predicate (i.e. for which the Predicate.filter() returns true.) 68 | * 69 | * @return The next tuple that passes the filter, or null if there are no 70 | * more tuples 71 | * @see Predicate#filter 72 | */ 73 | protected Tuple fetchNext() throws NoSuchElementException, TransactionAbortedException, DbException { 74 | // some code goes here 75 | Tuple tuple; 76 | while (this.child.hasNext()) { 77 | tuple = this.child.next(); 78 | if (tuple != null) { 79 | if (this.predicate.filter(tuple)) { 80 | return tuple; 81 | } 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | @Override 88 | public OpIterator[] getChildren() { 89 | // some code goes here 90 | return new OpIterator[] { this.child }; 91 | } 92 | 93 | @Override 94 | public void setChildren(OpIterator[] children) { 95 | // some code goes here 96 | this.child = children[0]; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/IndexOpIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.DbException; 5 | 6 | import java.util.*; 7 | 8 | /** IndexDBIterator is the interface that index access methods 9 | implement in SimpleDb. 10 | */ 11 | public interface IndexOpIterator extends OpIterator { 12 | /** Open the access method such that when getNext is called, it 13 | iterates through the tuples that satisfy ipred. 14 | @param ipred The predicate that is used to scan the index. 15 | */ 16 | void open(IndexPredicate ipred) throws NoSuchElementException, DbException, TransactionAbortedException; 17 | 18 | /** Begin a new index scan with the specified predicate. 19 | @param ipred The predicate that is used to scan the index. 20 | */ 21 | void rewind(IndexPredicate ipred) throws DbException, TransactionAbortedException; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/IndexPredicate.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.storage.Field; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * IndexPredicate compares a field which has index on it against a given value 9 | * @see IndexOpIterator 10 | */ 11 | public class IndexPredicate implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | private final Predicate.Op op; 16 | private final Field fieldvalue; 17 | 18 | /** 19 | * Constructor. 20 | * 21 | * @param fvalue The value that the predicate compares against. 22 | * @param op The operation to apply (as defined in Predicate.Op); either 23 | * Predicate.Op.GREATER_THAN, Predicate.Op.LESS_THAN, Predicate.Op.EQUAL, 24 | * Predicate.Op.GREATER_THAN_OR_EQ, or Predicate.Op.LESS_THAN_OR_EQ 25 | * @see Predicate 26 | */ 27 | public IndexPredicate(Predicate.Op op, Field fvalue) { 28 | this.op = op; 29 | this.fieldvalue = fvalue; 30 | } 31 | 32 | public Field getField() { 33 | return fieldvalue; 34 | } 35 | 36 | public Predicate.Op getOp() { 37 | return op; 38 | } 39 | 40 | /** Return true if the fieldvalue in the supplied predicate 41 | is satisfied by this predicate's fieldvalue and 42 | operator. 43 | @param ipd The field to compare against. 44 | */ 45 | public boolean equals(IndexPredicate ipd) { 46 | if (ipd == null) 47 | return false; 48 | return (op.equals(ipd.op) && fieldvalue.equals(ipd.fieldvalue)); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Insert.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.common.Database; 4 | import simpledb.common.DbException; 5 | import simpledb.common.Type; 6 | import simpledb.storage.BufferPool; 7 | import simpledb.storage.IntField; 8 | import simpledb.storage.Tuple; 9 | import simpledb.storage.TupleDesc; 10 | import simpledb.transaction.TransactionAbortedException; 11 | import simpledb.transaction.TransactionId; 12 | 13 | import javax.xml.crypto.Data; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | /** 18 | * Inserts tuples read from the child operator into the tableId specified in the 19 | * constructor 20 | */ 21 | public class Insert extends Operator { 22 | 23 | private static final long serialVersionUID = 1L; 24 | 25 | private TransactionId tid; 26 | private OpIterator child; 27 | private int tableId; 28 | private TupleDesc td; 29 | private boolean isFetched; 30 | 31 | /** 32 | * Constructor. 33 | * 34 | * @param t 35 | * The transaction running the insert. 36 | * @param child 37 | * The child operator from which to read tuples to be inserted. 38 | * @param tableId 39 | * The table in which to insert tuples. 40 | * @throws DbException 41 | * if TupleDesc of child differs from table into which we are to 42 | * insert. 43 | */ 44 | public Insert(TransactionId t, OpIterator child, int tableId) throws DbException { 45 | // some code goes here 46 | this.tid = t; 47 | this.child = child; 48 | this.tableId = tableId; 49 | final Type[] types = new Type[] { Type.INT_TYPE }; 50 | this.td = new TupleDesc(types); 51 | } 52 | 53 | public TupleDesc getTupleDesc() { 54 | // some code goes here 55 | return this.td; 56 | } 57 | 58 | public void open() throws DbException, TransactionAbortedException { 59 | // some code goes here 60 | this.child.open(); 61 | super.open(); 62 | isFetched = false; 63 | } 64 | 65 | public void close() { 66 | // some code goes here 67 | this.child.close(); 68 | super.close(); 69 | } 70 | 71 | public void rewind() throws DbException, TransactionAbortedException { 72 | // some code goes here 73 | close(); 74 | open(); 75 | } 76 | 77 | /** 78 | * Inserts tuples read from child into the tableId specified by the 79 | * constructor. It returns a one field tuple containing the number of 80 | * inserted records. Inserts should be passed through BufferPool. An 81 | * instances of BufferPool is available via Database.getBufferPool(). Note 82 | * that insert DOES NOT need check to see if a particular tuple is a 83 | * duplicate before inserting it. 84 | * 85 | * @return A 1-field tuple containing the number of inserted records, or 86 | * null if called more than once. 87 | * @see Database#getBufferPool 88 | * @see BufferPool#insertTuple 89 | */ 90 | protected Tuple fetchNext() throws TransactionAbortedException, DbException { 91 | // some code goes here 92 | int cnt = 0; 93 | while (this.child.hasNext()) { 94 | final Tuple next = this.child.next(); 95 | try { 96 | Database.getBufferPool().insertTuple(this.tid, this.tableId, next); 97 | } catch (Exception e) { 98 | e.printStackTrace(); 99 | System.out.println("Error happen when insert tuple:" + e.getMessage()); 100 | } 101 | cnt++; 102 | } 103 | if (cnt == 0 && isFetched) { 104 | return null; 105 | } 106 | isFetched = true; 107 | final Tuple result = new Tuple(this.td); 108 | result.setField(0, new IntField(cnt)); 109 | return result; 110 | } 111 | 112 | @Override 113 | public OpIterator[] getChildren() { 114 | // some code goes here 115 | return new OpIterator[] { this.child }; 116 | } 117 | 118 | @Override 119 | public void setChildren(OpIterator[] children) { 120 | // some code goes here 121 | if (children.length > 0) { 122 | this.child = children[0]; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/JoinPredicate.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.storage.Tuple; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * JoinPredicate compares fields of two tuples using a predicate. JoinPredicate 9 | * is most likely used by the Join operator. 10 | */ 11 | public class JoinPredicate implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | private int field1; 16 | private int field2; 17 | private Predicate.Op op; 18 | 19 | /** 20 | * Constructor -- create a new predicate over two fields of two tuples. 21 | * 22 | * @param field1 23 | * The field index into the first tuple in the predicate 24 | * @param field2 25 | * The field index into the second tuple in the predicate 26 | * @param op 27 | * The operation to apply (as defined in Predicate.Op); either 28 | * Predicate.Op.GREATER_THAN, Predicate.Op.LESS_THAN, 29 | * Predicate.Op.EQUAL, Predicate.Op.GREATER_THAN_OR_EQ, or 30 | * Predicate.Op.LESS_THAN_OR_EQ 31 | * @see Predicate 32 | */ 33 | public JoinPredicate(int field1, Predicate.Op op, int field2) { 34 | // some code goes here 35 | this.field1 = field1; 36 | this.field2 = field2; 37 | this.op = op; 38 | } 39 | 40 | /** 41 | * Apply the predicate to the two specified tuples. The comparison can be 42 | * made through Field's compare method. 43 | * 44 | * @return true if the tuples satisfy the predicate. 45 | */ 46 | public boolean filter(Tuple t1, Tuple t2) { 47 | // some code goes here 48 | return t1.getField(this.field1).compare(this.op, t2.getField(this.getField2())); 49 | } 50 | 51 | public int getField1() { 52 | // some code goes here 53 | return this.field1; 54 | } 55 | 56 | public int getField2() { 57 | // some code goes here 58 | return this.field2; 59 | } 60 | 61 | public Predicate.Op getOperator() { 62 | // some code goes here 63 | return this.op; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/OpIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.DbException; 5 | import simpledb.storage.Tuple; 6 | import simpledb.storage.TupleDesc; 7 | 8 | import java.io.Serializable; 9 | import java.util.*; 10 | 11 | /** 12 | * OpIterator is the iterator interface that all SimpleDB operators should 13 | * implement. If the iterator is not open, none of the methods should work, 14 | * and should throw an IllegalStateException. In addition to any 15 | * resource allocation/deallocation, an open method should call any 16 | * child iterator open methods, and in a close method, an iterator 17 | * should call its children's close methods. 18 | */ 19 | public interface OpIterator extends Serializable { 20 | /** 21 | * Opens the iterator. This must be called before any of the other methods. 22 | * @throws DbException when there are problems opening/accessing the database. 23 | */ 24 | void open() throws DbException, TransactionAbortedException; 25 | 26 | /** Returns true if the iterator has more tuples. 27 | * @return true f the iterator has more tuples. 28 | * @throws IllegalStateException If the iterator has not been opened 29 | */ 30 | boolean hasNext() throws DbException, TransactionAbortedException; 31 | 32 | /** 33 | * Returns the next tuple from the operator (typically implementing by reading 34 | * from a child operator or an access method). 35 | * 36 | * @return the next tuple in the iteration. 37 | * @throws NoSuchElementException if there are no more tuples. 38 | * @throws IllegalStateException If the iterator has not been opened 39 | */ 40 | Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException; 41 | 42 | /** 43 | * Resets the iterator to the start. 44 | * @throws DbException when rewind is unsupported. 45 | * @throws IllegalStateException If the iterator has not been opened 46 | */ 47 | void rewind() throws DbException, TransactionAbortedException; 48 | 49 | /** 50 | * Returns the TupleDesc associated with this OpIterator. 51 | * @return the TupleDesc associated with this OpIterator. 52 | */ 53 | TupleDesc getTupleDesc(); 54 | 55 | /** 56 | * Closes the iterator. When the iterator is closed, calling next(), 57 | * hasNext(), or rewind() should fail by throwing IllegalStateException. 58 | */ 59 | void close(); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Operator.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.DbException; 5 | import simpledb.storage.Tuple; 6 | import simpledb.storage.TupleDesc; 7 | 8 | import java.util.NoSuchElementException; 9 | 10 | /** 11 | * Abstract class for implementing operators. It handles close, 12 | * next and hasNext. Subclasses only need to implement 13 | * open and readNext. 14 | */ 15 | public abstract class Operator implements OpIterator { 16 | 17 | private static final long serialVersionUID = 1L; 18 | 19 | public boolean hasNext() throws DbException, TransactionAbortedException { 20 | if (!this.open) 21 | throw new IllegalStateException("Operator not yet open"); 22 | 23 | if (next == null) 24 | next = fetchNext(); 25 | return next != null; 26 | } 27 | 28 | public Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException { 29 | if (next == null) { 30 | next = fetchNext(); 31 | if (next == null) 32 | throw new NoSuchElementException(); 33 | } 34 | 35 | Tuple result = next; 36 | next = null; 37 | return result; 38 | } 39 | 40 | /** 41 | * Returns the next Tuple in the iterator, or null if the iteration is 42 | * finished. Operator uses this method to implement both next 43 | * and hasNext. 44 | * 45 | * @return the next Tuple in the iterator, or null if the iteration is 46 | * finished. 47 | */ 48 | protected abstract Tuple fetchNext() throws DbException, TransactionAbortedException; 49 | 50 | /** 51 | * Closes this iterator. If overridden by a subclass, they should call 52 | * super.close() in order for Operator's internal state to be consistent. 53 | */ 54 | public void close() { 55 | // Ensures that a future call to next() will fail 56 | next = null; 57 | this.open = false; 58 | } 59 | 60 | private Tuple next = null; 61 | private boolean open = false; 62 | private int estimatedCardinality = 0; 63 | 64 | public void open() throws DbException, TransactionAbortedException { 65 | this.open = true; 66 | } 67 | 68 | /** 69 | * @return return the children DbIterators of this operator. If there is 70 | * only one child, return an array of only one element. For join 71 | * operators, the order of the children is not important. But they 72 | * should be consistent among multiple calls. 73 | * */ 74 | public abstract OpIterator[] getChildren(); 75 | 76 | /** 77 | * Set the children(child) of this operator. If the operator has only one 78 | * child, children[0] should be used. If the operator is a join, children[0] 79 | * and children[1] should be used. 80 | * 81 | * 82 | * @param children 83 | * the DbIterators which are to be set as the children(child) of 84 | * this operator 85 | * */ 86 | public abstract void setChildren(OpIterator[] children); 87 | 88 | /** 89 | * @return return the TupleDesc of the output tuples of this operator 90 | * */ 91 | public abstract TupleDesc getTupleDesc(); 92 | 93 | /** 94 | * @return The estimated cardinality of this operator. Will only be used in 95 | * lab7 96 | * */ 97 | public int getEstimatedCardinality() { 98 | return this.estimatedCardinality; 99 | } 100 | 101 | /** 102 | * @param card 103 | * The estimated cardinality of this operator Will only be used 104 | * in lab7 105 | * */ 106 | public void setEstimatedCardinality(int card) { 107 | this.estimatedCardinality = card; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/OrderBy.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.DbException; 5 | import simpledb.storage.Field; 6 | import simpledb.storage.Tuple; 7 | import simpledb.storage.TupleDesc; 8 | 9 | import java.util.*; 10 | 11 | /** 12 | * OrderBy is an operator that implements a relational ORDER BY. 13 | */ 14 | public class OrderBy extends Operator { 15 | 16 | private static final long serialVersionUID = 1L; 17 | private OpIterator child; 18 | private final TupleDesc td; 19 | private final List childTups = new ArrayList<>(); 20 | private final int orderByField; 21 | private final String orderByFieldName; 22 | private Iterator it; 23 | private final boolean asc; 24 | 25 | /** 26 | * Creates a new OrderBy node over the tuples from the iterator. 27 | * 28 | * @param orderbyField 29 | * the field to which the sort is applied. 30 | * @param asc 31 | * true if the sort order is ascending. 32 | * @param child 33 | * the tuples to sort. 34 | */ 35 | public OrderBy(int orderbyField, boolean asc, OpIterator child) { 36 | this.child = child; 37 | td = child.getTupleDesc(); 38 | this.orderByField = orderbyField; 39 | this.orderByFieldName = td.getFieldName(orderbyField); 40 | this.asc = asc; 41 | } 42 | 43 | public boolean isASC() { 44 | return this.asc; 45 | } 46 | 47 | public int getOrderByField() { 48 | return this.orderByField; 49 | } 50 | 51 | public String getOrderFieldName() { 52 | return this.orderByFieldName; 53 | } 54 | 55 | public TupleDesc getTupleDesc() { 56 | return td; 57 | } 58 | 59 | public void open() throws DbException, NoSuchElementException, TransactionAbortedException { 60 | child.open(); 61 | // load all the tuples in a collection, and sort it 62 | while (child.hasNext()) 63 | childTups.add(child.next()); 64 | childTups.sort(new TupleComparator(orderByField, asc)); 65 | it = childTups.iterator(); 66 | super.open(); 67 | } 68 | 69 | public void close() { 70 | super.close(); 71 | it = null; 72 | } 73 | 74 | public void rewind() { 75 | it = childTups.iterator(); 76 | } 77 | 78 | /** 79 | * Operator.fetchNext implementation. Returns tuples from the child operator 80 | * in order 81 | * 82 | * @return The next tuple in the ordering, or null if there are no more 83 | * tuples 84 | */ 85 | protected Tuple fetchNext() throws NoSuchElementException { 86 | if (it != null && it.hasNext()) { 87 | return it.next(); 88 | } else 89 | return null; 90 | } 91 | 92 | @Override 93 | public OpIterator[] getChildren() { 94 | return new OpIterator[] { this.child }; 95 | } 96 | 97 | @Override 98 | public void setChildren(OpIterator[] children) { 99 | this.child = children[0]; 100 | } 101 | 102 | } 103 | 104 | class TupleComparator implements Comparator { 105 | final int field; 106 | final boolean asc; 107 | 108 | public TupleComparator(int field, boolean asc) { 109 | this.field = field; 110 | this.asc = asc; 111 | } 112 | 113 | public int compare(Tuple o1, Tuple o2) { 114 | Field t1 = (o1).getField(field); 115 | Field t2 = (o2).getField(field); 116 | if (t1.compare(Predicate.Op.EQUALS, t2)) 117 | return 0; 118 | if (t1.compare(Predicate.Op.GREATER_THAN, t2)) 119 | return asc ? 1 : -1; 120 | else 121 | return asc ? -1 : 1; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/PlanCache.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.optimizer.LogicalJoinNode; 4 | 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | /** A PlanCache is a helper class that can be used to store the best 11 | * way to order a given set of joins */ 12 | public class PlanCache { 13 | final Map, List> bestOrders = new HashMap<>(); 14 | final Map, Double> bestCosts = new HashMap<>(); 15 | final Map, Integer> bestCardinalities = new HashMap<>(); 16 | 17 | /** Add a new cost, cardinality and ordering for a particular join set. Does not verify that the 18 | new cost is less than any previously added cost -- simply adds or replaces an existing plan for the 19 | specified join set 20 | @param s the set of joins for which a new ordering (plan) is being added 21 | @param cost the estimated cost of the specified plan 22 | @param card the estimatied cardinality of the specified plan 23 | @param order the ordering of the joins in the plan 24 | */ 25 | public void addPlan(Set s, double cost, int card, List order) { 26 | bestOrders.put(s, order); 27 | bestCosts.put(s, cost); 28 | bestCardinalities.put(s, card); 29 | } 30 | 31 | /** Find the best join order in the cache for the specified plan 32 | @param s the set of joins to look up the best order for 33 | @return the best order for s in the cache 34 | */ 35 | public List getOrder(Set s) { 36 | return bestOrders.get(s); 37 | } 38 | 39 | /** Find the cost of the best join order in the cache for the specified plan 40 | @param s the set of joins to look up the best cost for 41 | @return the cost of the best order for s in the cache 42 | */ 43 | public double getCost(Set s) { 44 | return bestCosts.get(s); 45 | } 46 | 47 | /** Find the cardinality of the best join order in the cache for the specified plan 48 | @param s the set of joins to look up the best cardinality for 49 | @return the cardinality of the best order for s in the cache 50 | */ 51 | public int getCard(Set s) { 52 | return bestCardinalities.get(s); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Predicate.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.storage.Field; 4 | import simpledb.storage.Tuple; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * Predicate compares tuples to a specified Field value. 10 | */ 11 | public class Predicate implements Serializable { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | /** Constants used for return codes in Field.compare */ 16 | public enum Op implements Serializable { 17 | EQUALS, GREATER_THAN, LESS_THAN, LESS_THAN_OR_EQ, GREATER_THAN_OR_EQ, LIKE, NOT_EQUALS; 18 | 19 | /** 20 | * Interface to access operations by integer value for command-line 21 | * convenience. 22 | * 23 | * @param i 24 | * a valid integer Op index 25 | */ 26 | public static Op getOp(int i) { 27 | return values()[i]; 28 | } 29 | 30 | public String toString() { 31 | if (this == EQUALS) 32 | return "="; 33 | if (this == GREATER_THAN) 34 | return ">"; 35 | if (this == LESS_THAN) 36 | return "<"; 37 | if (this == LESS_THAN_OR_EQ) 38 | return "<="; 39 | if (this == GREATER_THAN_OR_EQ) 40 | return ">="; 41 | if (this == LIKE) 42 | return "LIKE"; 43 | if (this == NOT_EQUALS) 44 | return "<>"; 45 | throw new IllegalStateException("impossible to reach here"); 46 | } 47 | 48 | } 49 | 50 | private int field; 51 | private Op op; 52 | private Field operand; 53 | 54 | /** 55 | * Constructor. 56 | * 57 | * @param field 58 | * field number of passed in tuples to compare against. 59 | * @param op 60 | * operation to use for comparison 61 | * @param operand 62 | * field value to compare passed in tuples to 63 | */ 64 | public Predicate(int field, Op op, Field operand) { 65 | // some code goes here 66 | this.field = field; 67 | this.op = op; 68 | this.operand = operand; 69 | } 70 | 71 | /** 72 | * @return the field number 73 | */ 74 | public int getField() { 75 | // some code goes here 76 | return this.field; 77 | } 78 | 79 | /** 80 | * @return the operator 81 | */ 82 | public Op getOp() { 83 | // some code goes here 84 | return this.op; 85 | } 86 | 87 | /** 88 | * @return the operand 89 | */ 90 | public Field getOperand() { 91 | // some code goes here 92 | return this.operand; 93 | } 94 | 95 | /** 96 | * Compares the field number of t specified in the constructor to the 97 | * operand field specified in the constructor using the operator specific in 98 | * the constructor. The comparison can be made through Field's compare 99 | * method. 100 | * 101 | * @param t 102 | * The tuple to compare against 103 | * @return true if the comparison is true, false otherwise. 104 | */ 105 | public boolean filter(Tuple t) { 106 | // some code goes here 107 | final Field field1 = t.getField(this.field); 108 | final Field field2 = this.operand; 109 | return field1.compare(this.op, field2); 110 | } 111 | 112 | /** 113 | * Returns something useful, like "f = field_id op = op_string operand = 114 | * operand_string" 115 | */ 116 | @Override 117 | public String toString() { 118 | return "Predicate{" + "field=" + field + ", op=" + op + ", operand=" + operand + '}'; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Project.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.transaction.TransactionAbortedException; 4 | import simpledb.common.Type; 5 | import simpledb.common.DbException; 6 | import simpledb.storage.Tuple; 7 | import simpledb.storage.TupleDesc; 8 | 9 | import java.util.*; 10 | 11 | /** 12 | * Project is an operator that implements a relational projection. 13 | */ 14 | public class Project extends Operator { 15 | 16 | private static final long serialVersionUID = 1L; 17 | private OpIterator child; 18 | private final TupleDesc td; 19 | private final List outFieldIds; 20 | 21 | /** 22 | * Constructor accepts a child operator to read tuples to apply projection 23 | * to and a list of fields in output tuple 24 | * 25 | * @param fieldList The ids of the fields child's tupleDesc to project out 26 | * @param typesList the types of the fields in the final projection 27 | * @param child The child operator 28 | */ 29 | public Project(List fieldList, List typesList, OpIterator child) { 30 | this(fieldList, typesList.toArray(new Type[] {}), child); 31 | } 32 | 33 | public Project(List fieldList, Type[] types, OpIterator child) { 34 | this.child = child; 35 | outFieldIds = fieldList; 36 | String[] fieldAr = new String[fieldList.size()]; 37 | TupleDesc childtd = child.getTupleDesc(); 38 | 39 | for (int i = 0; i < fieldAr.length; i++) { 40 | fieldAr[i] = childtd.getFieldName(fieldList.get(i)); 41 | } 42 | td = new TupleDesc(types, fieldAr); 43 | } 44 | 45 | public TupleDesc getTupleDesc() { 46 | return td; 47 | } 48 | 49 | public void open() throws DbException, NoSuchElementException, 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, TransactionAbortedException, DbException { 70 | if (!child.hasNext()) 71 | return null; 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 | 81 | @Override 82 | public OpIterator[] getChildren() { 83 | return new OpIterator[] { this.child }; 84 | } 85 | 86 | @Override 87 | public void setChildren(OpIterator[] children) { 88 | if (this.child != children[0]) { 89 | this.child = children[0]; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/Query.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.optimizer.LogicalPlan; 4 | import simpledb.transaction.TransactionAbortedException; 5 | import simpledb.transaction.TransactionId; 6 | import simpledb.common.DbException; 7 | import simpledb.storage.Tuple; 8 | import simpledb.storage.TupleDesc; 9 | 10 | import java.io.*; 11 | import java.util.*; 12 | 13 | /** 14 | * Query is a wrapper class to manage the execution of queries. It takes a query 15 | * plan in the form of a high level OpIterator (built by initiating the 16 | * constructors of query plans) and runs it as a part of a specified 17 | * transaction. 18 | * 19 | * @author Sam Madden 20 | */ 21 | 22 | public class Query implements Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | transient private OpIterator op; 27 | transient private LogicalPlan logicalPlan; 28 | final TransactionId tid; 29 | transient private boolean started = false; 30 | 31 | public TransactionId getTransactionId() { 32 | return this.tid; 33 | } 34 | 35 | public void setLogicalPlan(LogicalPlan lp) { 36 | this.logicalPlan = lp; 37 | } 38 | 39 | public LogicalPlan getLogicalPlan() { 40 | return this.logicalPlan; 41 | } 42 | 43 | public void setPhysicalPlan(OpIterator pp) { 44 | this.op = pp; 45 | } 46 | 47 | public OpIterator getPhysicalPlan() { 48 | return this.op; 49 | } 50 | 51 | public Query(TransactionId t) { 52 | tid = t; 53 | } 54 | 55 | public Query(OpIterator root, TransactionId t) { 56 | op = root; 57 | tid = t; 58 | } 59 | 60 | public void start() throws DbException, TransactionAbortedException { 61 | op.open(); 62 | 63 | started = true; 64 | } 65 | 66 | public TupleDesc getOutputTupleDesc() { 67 | return this.op.getTupleDesc(); 68 | } 69 | 70 | /** @return true if there are more tuples remaining. */ 71 | public boolean hasNext() throws DbException, TransactionAbortedException { 72 | return op.hasNext(); 73 | } 74 | 75 | /** 76 | * Returns the next tuple, or throws NoSuchElementException if the iterator 77 | * is closed. 78 | * 79 | * @return The next tuple in the iterator 80 | * @throws DbException 81 | * If there is an error in the database system 82 | * @throws NoSuchElementException 83 | * If the iterator has finished iterating 84 | * @throws TransactionAbortedException 85 | * If the transaction is aborted (e.g., due to a deadlock) 86 | */ 87 | public Tuple next() throws DbException, NoSuchElementException, TransactionAbortedException { 88 | if (!started) 89 | throw new DbException("Database not started."); 90 | 91 | return op.next(); 92 | } 93 | 94 | /** Close the iterator */ 95 | public void close() { 96 | op.close(); 97 | started = false; 98 | } 99 | 100 | public void execute() throws DbException, TransactionAbortedException { 101 | TupleDesc td = this.getOutputTupleDesc(); 102 | 103 | StringBuilder names = new StringBuilder(); 104 | for (int i = 0; i < td.numFields(); i++) { 105 | names.append(td.getFieldName(i)).append("\t"); 106 | } 107 | System.out.println(names); 108 | for (int i = 0; i < names.length() + td.numFields() * 4; i++) { 109 | System.out.print("-"); 110 | } 111 | System.out.println(); 112 | 113 | this.start(); 114 | int cnt = 0; 115 | while (this.hasNext()) { 116 | Tuple tup = this.next(); 117 | System.out.println(tup); 118 | cnt++; 119 | } 120 | System.out.println("\n " + cnt + " rows."); 121 | this.close(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/simpledb/execution/StringAggregator.java: -------------------------------------------------------------------------------- 1 | package simpledb.execution; 2 | 3 | import simpledb.common.Type; 4 | import simpledb.storage.*; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | /** 12 | * Knows how to compute some aggregate over a set of StringFields. 13 | */ 14 | public class StringAggregator implements Aggregator { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | private Map groupMap; 19 | 20 | // Group by field 21 | private int gbField; 22 | private Type gbFieldType; 23 | // Aggregation field 24 | private int agField; 25 | // Aggregation operation 26 | private Op op; 27 | private Field DEFAULT_FIELD = new StringField("Default", 10); 28 | 29 | private TupleDesc td; 30 | 31 | /** 32 | * Aggregate constructor 33 | * @param gbfield the 0-based index of the group-by field in the tuple, or NO_GROUPING if there is no grouping 34 | * @param gbfieldtype the type of the group by field (e.g., Type.INT_TYPE), or null if there is no grouping 35 | * @param afield the 0-based index of the aggregate field in the tuple 36 | * @param what aggregation operator to use -- only supports COUNT 37 | * @throws IllegalArgumentException if what != COUNT 38 | */ 39 | 40 | public StringAggregator(int gbfield, Type gbfieldtype, int afield, Op what) { 41 | // some code goes here 42 | if (what != Op.COUNT) { 43 | throw new IllegalArgumentException("Err in StringAggregator: What != Count"); 44 | } 45 | this.groupMap = new HashMap<>(); 46 | this.gbField = gbfield; 47 | this.agField = afield; 48 | this.op = what; 49 | this.gbFieldType = gbfieldtype; 50 | } 51 | 52 | /** 53 | * Merge a new tuple into the aggregate, grouping as indicated in the constructor 54 | * @param tup the Tuple containing an aggregate field and a group-by field 55 | */ 56 | public void mergeTupleIntoGroup(Tuple tup) { 57 | // some code goes here 58 | if (this.td == null) { 59 | buildTupleDesc(tup.getTupleDesc()); 60 | } 61 | final Field gbField = tup.getField(this.gbField); 62 | final Field target = (this.gbField == NO_GROUPING ? DEFAULT_FIELD : gbField); 63 | this.groupMap.put(target, this.groupMap.getOrDefault(target, 0) + 1); 64 | } 65 | 66 | public void buildTupleDesc(final TupleDesc originTd) { 67 | // some code goes here 68 | if (this.gbField == NO_GROUPING) { 69 | Type[] types = new Type[] { Type.INT_TYPE }; 70 | String[] names = new String[] { originTd.getFieldName(this.gbField) }; 71 | this.td = new TupleDesc(types, names); 72 | } else { 73 | Type[] types = new Type[] { this.gbFieldType, Type.INT_TYPE }; 74 | String[] names = new String[] { originTd.getFieldName(this.gbField), originTd.getFieldName(this.agField) }; 75 | this.td = new TupleDesc(types, names); 76 | } 77 | } 78 | 79 | /** 80 | * Create a OpIterator over group aggregate results. 81 | * 82 | * @return a OpIterator whose tuples are the pair (groupVal, 83 | * aggregateVal) if using group, or a single (aggregateVal) if no 84 | * grouping. The aggregateVal is determined by the type of 85 | * aggregate specified in the constructor. 86 | */ 87 | public OpIterator iterator() { 88 | // some code goes here 89 | final List tuples = new ArrayList<>(); 90 | if (this.gbField != NO_GROUPING) { 91 | this.groupMap.forEach((key, cnt) -> { 92 | final Tuple tuple = new Tuple(this.td); 93 | tuple.setField(0, key); 94 | tuple.setField(1, new IntField(cnt)); 95 | tuples.add(tuple); 96 | }); 97 | } else { 98 | final Tuple tuple = new Tuple(this.td); 99 | tuple.setField(0, new IntField(this.groupMap.get(DEFAULT_FIELD))); 100 | tuples.add(tuple); 101 | } 102 | return new TupleIterator(this.td, tuples); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/simpledb/index/BTreePageId.java: -------------------------------------------------------------------------------- 1 | package simpledb.index; 2 | 3 | import simpledb.storage.BufferPool; 4 | import simpledb.storage.PageId; 5 | 6 | import java.util.Objects; 7 | 8 | /** Unique identifier for BTreeInternalPage, BTreeLeafPage, BTreeHeaderPage 9 | * and BTreeRootPtrPage objects. 10 | */ 11 | public class BTreePageId implements PageId { 12 | 13 | public final static int ROOT_PTR = 0; 14 | public final static int INTERNAL = 1; 15 | public final static int LEAF = 2; 16 | public final static int HEADER = 3; 17 | 18 | private final int tableId; 19 | private final int pgNo; 20 | private final int pgcateg; 21 | 22 | static public String categToString(int categ) { 23 | switch (categ) { 24 | case ROOT_PTR: 25 | return "ROOT_PTR"; 26 | case INTERNAL: 27 | return "INTERNAL"; 28 | case LEAF: 29 | return "LEAF"; 30 | case HEADER: 31 | return "HEADER"; 32 | default: 33 | throw new IllegalArgumentException("categ"); 34 | } 35 | } 36 | 37 | /** 38 | * Constructor. Create a page id structure for a specific page of a 39 | * specific table. 40 | * 41 | * @param tableId The table that is being referenced 42 | * @param pgNo The page number in that table. 43 | * @param pgcateg which kind of page it is 44 | */ 45 | public BTreePageId(int tableId, int pgNo, int pgcateg) { 46 | this.tableId = tableId; 47 | this.pgNo = pgNo; 48 | this.pgcateg = pgcateg; 49 | } 50 | 51 | /** @return the table associated with this PageId */ 52 | public int getTableId() { 53 | return tableId; 54 | } 55 | 56 | /** 57 | * @return the page number in the table getTableId() associated with 58 | * this PageId 59 | */ 60 | public int getPageNumber() { 61 | return pgNo; 62 | } 63 | 64 | /** 65 | * @return the category of this page 66 | */ 67 | public int pgcateg() { 68 | return pgcateg; 69 | } 70 | 71 | /** 72 | * @return a hash code for this page, represented by the combination of 73 | * the table number, page number, and pgcateg (needed if a PageId is used as a 74 | * key in a hash table in the BufferPool, for example.) 75 | * @see BufferPool 76 | */ 77 | public int hashCode() { 78 | return Objects.hash(tableId, pgNo, pgcateg); 79 | } 80 | 81 | /** 82 | * Compares one PageId to another. 83 | * 84 | * @param o The object to compare against (must be a PageId) 85 | * @return true if the objects are equal (e.g., page numbers, table 86 | * ids and pgcateg are the same) 87 | */ 88 | public boolean equals(Object o) { 89 | if (!(o instanceof BTreePageId)) 90 | return false; 91 | BTreePageId p = (BTreePageId) o; 92 | return tableId == p.tableId && pgNo == p.pgNo && pgcateg == p.pgcateg; 93 | } 94 | 95 | public String toString() { 96 | 97 | return "(tableId: " + tableId + ", pgNo: " + pgNo + ", pgcateg: " + categToString(pgcateg) + ")"; 98 | } 99 | 100 | /** 101 | * Return a representation of this object as an array of 102 | * integers, for writing to disk. Size of returned array must contain 103 | * number of integers that corresponds to number of args to one of the 104 | * constructors. 105 | */ 106 | public int[] serialize() { 107 | int[] data = new int[3]; 108 | 109 | data[0] = tableId; 110 | data[1] = pgNo; 111 | data[2] = pgcateg; 112 | 113 | return data; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/CostCard.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import java.util.List; 4 | 5 | /** Class returned by {@link JoinOptimizer#computeCostAndCardOfSubplan} specifying the 6 | cost and cardinality of the optimal plan represented by plan. 7 | */ 8 | public class CostCard { 9 | /** The cost of the optimal subplan */ 10 | public double cost; 11 | /** The cardinality of the optimal subplan */ 12 | public int card; 13 | /** The optimal subplan */ 14 | public List plan; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/Histogram.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.execution.Predicate; 4 | 5 | public interface Histogram { 6 | 7 | /** 8 | * Add a value to the set of values that you are keeping a histogram of. 9 | * @param v Value to add to the histogram 10 | */ 11 | void addValue(final T v); 12 | 13 | /** 14 | * Estimate the selectivity of a particular predicate and operand on this table. 15 | * 16 | * For example, if "op" is "GREATER_THAN" and "v" is 5, 17 | * return your estimate of the fraction of elements that are greater than 5. 18 | * 19 | * @param op Operator 20 | * @param v Value 21 | * @return Predicted selectivity of this particular operator and value 22 | */ 23 | double estimateSelectivity(Predicate.Op op, T v); 24 | 25 | /** 26 | * @return 27 | * the average selectivity of this histogram. 28 | * 29 | * This is not an indispensable method to implement the basic 30 | * join optimization. It may be needed if you want to 31 | * implement a more efficient optimization 32 | * */ 33 | public double avgSelectivity(); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/LogicalFilterNode.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.execution.Predicate; 4 | 5 | /** A LogicalFilterNode represents the parameters of a filter in the WHERE clause of a query. 6 |

7 | Filter is of the form t.f p c 8 |

9 | Where t is a table, f is a field in t, p is a predicate, and c is a constant 10 | */ 11 | public class LogicalFilterNode { 12 | /** The alias of a table (or the name if no alias) over which the filter ranges */ 13 | public final String tableAlias; 14 | 15 | /** The predicate in the filter */ 16 | public final Predicate.Op p; 17 | 18 | /* The constant on the right side of the filter */ 19 | public final String c; 20 | 21 | /** The field from t which is in the filter. The pure name, without alias or tablename*/ 22 | public final String fieldPureName; 23 | 24 | public final String fieldQuantifiedName; 25 | 26 | public LogicalFilterNode(String table, String field, Predicate.Op pred, String constant) { 27 | tableAlias = table; 28 | p = pred; 29 | c = constant; 30 | String[] tmps = field.split("[.]"); 31 | if (tmps.length > 1) 32 | fieldPureName = tmps[tmps.length - 1]; 33 | else 34 | fieldPureName = field; 35 | this.fieldQuantifiedName = tableAlias + "." + fieldPureName; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/LogicalJoinNode.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.execution.Predicate; 4 | 5 | /** A LogicalJoinNode represens the state needed of a join of two 6 | * tables in a LogicalQueryPlan */ 7 | public class LogicalJoinNode { 8 | 9 | /** The first table to join (may be null). It's the alias of the table (if no alias, the true table name) */ 10 | public String t1Alias; 11 | 12 | /** The second table to join (may be null). It's the alias of the table, (if no alias, the true table name).*/ 13 | public String t2Alias; 14 | 15 | /** The name of the field in t1 to join with. It's the pure name of a field, rather that alias.field. */ 16 | public String f1PureName; 17 | 18 | public String f1QuantifiedName; 19 | 20 | /** The name of the field in t2 to join with. It's the pure name of a field.*/ 21 | public String f2PureName; 22 | 23 | public String f2QuantifiedName; 24 | 25 | /** The join predicate */ 26 | public Predicate.Op p; 27 | 28 | public LogicalJoinNode() { 29 | } 30 | 31 | public LogicalJoinNode(String table1, String table2, String joinField1, String joinField2, Predicate.Op pred) { 32 | t1Alias = table1; 33 | t2Alias = table2; 34 | String[] tmps = joinField1.split("[.]"); 35 | if (tmps.length > 1) 36 | f1PureName = tmps[tmps.length - 1]; 37 | else 38 | f1PureName = joinField1; 39 | tmps = joinField2.split("[.]"); 40 | if (tmps.length > 1) 41 | f2PureName = tmps[tmps.length - 1]; 42 | else 43 | f2PureName = joinField2; 44 | p = pred; 45 | this.f1QuantifiedName = t1Alias + "." + this.f1PureName; 46 | this.f2QuantifiedName = t2Alias + "." + this.f2PureName; 47 | } 48 | 49 | /** Return a new LogicalJoinNode with the inner and outer (t1.f1 50 | * and t2.f2) tables swapped. */ 51 | public LogicalJoinNode swapInnerOuter() { 52 | Predicate.Op newp; 53 | if (p == Predicate.Op.GREATER_THAN) 54 | newp = Predicate.Op.LESS_THAN; 55 | else if (p == Predicate.Op.GREATER_THAN_OR_EQ) 56 | newp = Predicate.Op.LESS_THAN_OR_EQ; 57 | else if (p == Predicate.Op.LESS_THAN) 58 | newp = Predicate.Op.GREATER_THAN; 59 | else if (p == Predicate.Op.LESS_THAN_OR_EQ) 60 | newp = Predicate.Op.GREATER_THAN_OR_EQ; 61 | else 62 | newp = p; 63 | 64 | return new LogicalJoinNode(t2Alias, t1Alias, f2PureName, f1PureName, newp); 65 | } 66 | 67 | @Override 68 | public boolean equals(Object o) { 69 | if (!(o instanceof LogicalJoinNode)) 70 | return false; 71 | LogicalJoinNode j2 = (LogicalJoinNode) o; 72 | return (j2.t1Alias.equals(t1Alias) || j2.t1Alias.equals(t2Alias)) 73 | && (j2.t2Alias.equals(t1Alias) || j2.t2Alias.equals(t2Alias)); 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return t1Alias + ":" + t2Alias;//+ ";" + f1 + " " + p + " " + f2; 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return t1Alias.hashCode() + t2Alias.hashCode() + f1PureName.hashCode() + f2PureName.hashCode(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/LogicalScanNode.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.common.Catalog; 4 | 5 | /** A LogicalScanNode represents table in the FROM list in a 6 | * LogicalQueryPlan */ 7 | public class LogicalScanNode { 8 | 9 | /** The name (alias) of the table as it is used in the query */ 10 | public final String alias; 11 | 12 | /** The table identifier (can be passed to {@link Catalog#getDatabaseFile}) 13 | * to retrieve a DbFile */ 14 | public final int t; 15 | 16 | public LogicalScanNode(int table, String tableAlias) { 17 | this.alias = tableAlias; 18 | this.t = table; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/LogicalSelectListNode.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 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 final String fname; 11 | 12 | /** The aggregation operation over the field (if any) */ 13 | public final String aggOp; 14 | 15 | public LogicalSelectListNode(String aggOp, String fname) { 16 | this.aggOp = aggOp; 17 | this.fname = fname; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/LogicalSubplanJoinNode.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.execution.OpIterator; 4 | import simpledb.execution.Predicate; 5 | 6 | /** A LogicalSubplanJoinNode represens the state needed of a join of a 7 | * table to a subplan in a LogicalQueryPlan -- inherits state from 8 | * {@link LogicalJoinNode}; t2 and f2 should always be null 9 | */ 10 | public class LogicalSubplanJoinNode extends LogicalJoinNode { 11 | 12 | /** The subplan (used on the inner) of the join */ 13 | final OpIterator subPlan; 14 | 15 | public LogicalSubplanJoinNode(String table1, String joinField1, OpIterator sp, Predicate.Op pred) { 16 | t1Alias = table1; 17 | String[] tmps = joinField1.split("[.]"); 18 | if (tmps.length > 1) 19 | f1PureName = tmps[tmps.length - 1]; 20 | else 21 | f1PureName = joinField1; 22 | f1QuantifiedName = t1Alias + "." + f1PureName; 23 | subPlan = sp; 24 | p = pred; 25 | } 26 | 27 | @Override 28 | public int hashCode() { 29 | return t1Alias.hashCode() + f1PureName.hashCode() + subPlan.hashCode(); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | LogicalJoinNode j2 = (LogicalJoinNode) o; 35 | if (!(o instanceof LogicalSubplanJoinNode)) 36 | return false; 37 | 38 | return (j2.t1Alias.equals(t1Alias) && j2.f1PureName.equals(f1PureName) && ((LogicalSubplanJoinNode) o).subPlan 39 | .equals(subPlan)); 40 | } 41 | 42 | public LogicalSubplanJoinNode swapInnerOuter() { 43 | return new LogicalSubplanJoinNode(t1Alias, f1PureName, subPlan, p); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/simpledb/optimizer/StringHistogram.java: -------------------------------------------------------------------------------- 1 | package simpledb.optimizer; 2 | 3 | import simpledb.execution.Predicate; 4 | 5 | /** 6 | * A class to represent a fixed-width histogram over a single String-based 7 | * field. 8 | */ 9 | public class StringHistogram implements Histogram { 10 | final IntHistogram hist; 11 | 12 | /** 13 | * Create a new StringHistogram with a specified number of buckets. 14 | *

15 | * Our implementation is written in terms of an IntHistogram by converting 16 | * each String to an integer. 17 | * 18 | * @param buckets 19 | * the number of buckets 20 | */ 21 | public StringHistogram(int buckets) { 22 | hist = new IntHistogram(buckets, minVal(), maxVal()); 23 | } 24 | 25 | /** 26 | * Convert a string to an integer, with the property that if the return 27 | * value(s1) < return value(s2), then s1 < s2 28 | */ 29 | private int stringToInt(String s) { 30 | int i; 31 | int v = 0; 32 | for (i = 3; i >= 0; i--) { 33 | if (s.length() > 3 - i) { 34 | int ci = s.charAt(3 - i); 35 | v += (ci) << (i * 8); 36 | } 37 | } 38 | 39 | // XXX: hack to avoid getting wrong results for 40 | // strings which don't output in the range min to max 41 | if (!(s.equals("") || s.equals("zzzz"))) { 42 | if (v < minVal()) { 43 | v = minVal(); 44 | } 45 | 46 | if (v > maxVal()) { 47 | v = maxVal(); 48 | } 49 | } 50 | 51 | return v; 52 | } 53 | 54 | /** @return the maximum value indexed by the histogram */ 55 | int maxVal() { 56 | return stringToInt("zzzz"); 57 | } 58 | 59 | /** @return the minimum value indexed by the histogram */ 60 | int minVal() { 61 | return stringToInt(""); 62 | } 63 | 64 | /** Add a new value to thte histogram */ 65 | public void addValue(String s) { 66 | int val = stringToInt(s); 67 | hist.addValue(val); 68 | } 69 | 70 | /** 71 | * Estimate the selectivity (as a double between 0 and 1) of the specified 72 | * predicate over the specified string 73 | * 74 | * @param op 75 | * The operation being applied 76 | * @param s 77 | * The string to apply op to 78 | */ 79 | public double estimateSelectivity(Predicate.Op op, String s) { 80 | int val = stringToInt(s); 81 | return hist.estimateSelectivity(op, val); 82 | } 83 | 84 | /** 85 | * @return the average selectivity of this histogram. 86 | * 87 | * This is not an indispensable method to implement the basic join 88 | * optimization. It may be needed if you want to implement a more 89 | * efficient optimization 90 | * */ 91 | public double avgSelectivity() { 92 | return hist.avgSelectivity(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/AbstractDbFileIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.common.DbException; 4 | import simpledb.transaction.TransactionAbortedException; 5 | 6 | import java.util.NoSuchElementException; 7 | 8 | /** Helper for implementing DbFileIterators. Handles hasNext()/next() logic. */ 9 | public abstract class AbstractDbFileIterator implements DbFileIterator { 10 | 11 | public boolean hasNext() throws DbException, TransactionAbortedException { 12 | if (next == null) 13 | next = readNext(); 14 | return next != null; 15 | } 16 | 17 | public Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException { 18 | if (next == null) { 19 | next = readNext(); 20 | if (next == null) 21 | throw new NoSuchElementException(); 22 | } 23 | 24 | Tuple result = next; 25 | next = null; 26 | return result; 27 | } 28 | 29 | /** If subclasses override this, they should call super.close(). */ 30 | public void close() { 31 | // Ensures that a future call to next() will fail 32 | next = null; 33 | } 34 | 35 | /** Reads the next tuple from the underlying source. 36 | @return the next Tuple in the iterator, null if the iteration is finished. */ 37 | protected abstract Tuple readNext() throws DbException, TransactionAbortedException; 38 | 39 | private Tuple next = null; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/DbFile.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.common.DbException; 4 | import simpledb.common.Catalog; 5 | import simpledb.transaction.TransactionAbortedException; 6 | import simpledb.transaction.TransactionId; 7 | 8 | import java.util.*; 9 | import java.io.*; 10 | 11 | /** 12 | * The interface for database files on disk. Each table is represented by a 13 | * single DbFile. DbFiles can fetch pages and iterate through tuples. Each 14 | * file has a unique id used to store metadata about the table in the Catalog. 15 | * DbFiles are generally accessed through the buffer pool, rather than directly 16 | * by operators. 17 | */ 18 | public interface DbFile { 19 | /** 20 | * Read the specified page from disk. 21 | * 22 | * @throws IllegalArgumentException if the page does not exist in this file. 23 | */ 24 | Page readPage(PageId id); 25 | 26 | /** 27 | * Push the specified page to disk. 28 | * 29 | * @param p The page to write. page.getId().pageno() specifies the offset into the file where the page should be written. 30 | * @throws IOException if the write fails 31 | * 32 | */ 33 | void writePage(Page p) throws IOException; 34 | 35 | /** 36 | * Inserts the specified tuple to the file on behalf of transaction. 37 | * This method will acquire a lock on the affected pages of the file, and 38 | * may block until the lock can be acquired. 39 | * 40 | * @param tid The transaction performing the update 41 | * @param t The tuple to add. This tuple should be updated to reflect that 42 | * it is now stored in this file. 43 | * @return An ArrayList contain the pages that were modified 44 | * @throws DbException if the tuple cannot be added 45 | * @throws IOException if the needed file can't be read/written 46 | */ 47 | List insertTuple(TransactionId tid, Tuple t) throws DbException, IOException, TransactionAbortedException; 48 | 49 | /** 50 | * Removes the specified tuple from the file on behalf of the specified 51 | * transaction. 52 | * This method will acquire a lock on the affected pages of the file, and 53 | * may block until the lock can be acquired. 54 | * 55 | * @param tid The transaction performing the update 56 | * @param t The tuple to delete. This tuple should be updated to reflect that 57 | * it is no longer stored on any page. 58 | * @return An ArrayList contain the pages that were modified 59 | * @throws DbException if the tuple cannot be deleted or is not a member 60 | * of the file 61 | */ 62 | List deleteTuple(TransactionId tid, Tuple t) throws DbException, IOException, TransactionAbortedException; 63 | 64 | /** 65 | * Returns an iterator over all the tuples stored in this DbFile. The 66 | * iterator must use {@link BufferPool#getPage}, rather than 67 | * {@link #readPage} to iterate through the pages. 68 | * 69 | * @return an iterator over all the tuples stored in this DbFile. 70 | */ 71 | DbFileIterator iterator(TransactionId tid); 72 | 73 | /** 74 | * Returns a unique ID used to identify this DbFile in the Catalog. This id 75 | * can be used to look up the table via {@link Catalog#getDatabaseFile} and 76 | * {@link Catalog#getTupleDesc}. 77 | *

78 | * Implementation note: you will need to generate this tableid somewhere, 79 | * ensure that each HeapFile has a "unique id," and that you always 80 | * return the same value for a particular HeapFile. A simple implementation 81 | * is to use the hash code of the absolute path of the file underlying 82 | * the HeapFile, i.e. f.getAbsoluteFile().hashCode(). 83 | * 84 | * @return an ID uniquely identifying this HeapFile. 85 | */ 86 | int getId(); 87 | 88 | /** 89 | * Returns the TupleDesc of the table stored in this DbFile. 90 | * @return TupleDesc of this DbFile. 91 | */ 92 | TupleDesc getTupleDesc(); 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/DbFileIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.common.DbException; 4 | import simpledb.transaction.TransactionAbortedException; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * DbFileIterator is the iterator interface that all SimpleDB Dbfile should 10 | * implement. 11 | */ 12 | public interface DbFileIterator { 13 | /** 14 | * Opens the iterator 15 | * @throws DbException when there are problems opening/accessing the database. 16 | */ 17 | void open() throws DbException, TransactionAbortedException; 18 | 19 | /** @return true if there are more tuples available, false if no more tuples or iterator isn't open. */ 20 | boolean hasNext() throws DbException, TransactionAbortedException; 21 | 22 | /** 23 | * Gets the next tuple from the operator (typically implementing by reading 24 | * from a child operator or an access method). 25 | * 26 | * @return The next tuple in the iterator. 27 | * @throws NoSuchElementException if there are no more tuples 28 | */ 29 | Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException; 30 | 31 | /** 32 | * Resets the iterator to the start. 33 | * @throws DbException When rewind is unsupported. 34 | */ 35 | void rewind() throws DbException, TransactionAbortedException; 36 | 37 | /** 38 | * Closes the iterator. 39 | */ 40 | void close(); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/Field.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.execution.Predicate; 4 | import simpledb.common.Type; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * Interface for values of fields in tuples in SimpleDB. 10 | */ 11 | public interface Field extends Serializable { 12 | /** 13 | * Write the bytes representing this field to the specified 14 | * DataOutputStream. 15 | * @see DataOutputStream 16 | * @param dos The DataOutputStream to write to. 17 | */ 18 | void serialize(DataOutputStream dos) throws IOException; 19 | 20 | /** 21 | * Compare the value of this field object to the passed in value. 22 | * @param op The operator 23 | * @param value The value to compare this Field to 24 | * @return Whether or not the comparison yields true. 25 | */ 26 | boolean compare(Predicate.Op op, Field value); 27 | 28 | /** 29 | * Returns the type of this field (see {@link Type#INT_TYPE} or {@link Type#STRING_TYPE} 30 | * @return type of this field 31 | */ 32 | Type getType(); 33 | 34 | /** 35 | * Hash code. 36 | * Different Field objects representing the same value should probably 37 | * return the same hashCode. 38 | */ 39 | int hashCode(); 40 | 41 | boolean equals(Object field); 42 | 43 | String toString(); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/HeapPageId.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | /** Unique identifier for HeapPage objects. */ 4 | public class HeapPageId implements PageId { 5 | 6 | private final int tableId; 7 | 8 | private final int pageNum; 9 | 10 | /** 11 | * Constructor. Create a page id structure for a specific page of a 12 | * specific table. 13 | * 14 | * @param tableId The table that is being referenced 15 | * @param pgNo The page number in that table. 16 | */ 17 | public HeapPageId(int tableId, int pgNo) { 18 | // some code goes here 19 | this.tableId = tableId; 20 | this.pageNum = pgNo; 21 | } 22 | 23 | /** 24 | * @return the table associated with this PageId 25 | */ 26 | public int getTableId() { 27 | // some code goes here 28 | return this.tableId; 29 | } 30 | 31 | /** 32 | * @return the page number in the table getTableId() associated with 33 | * this PageId 34 | */ 35 | public int getPageNumber() { 36 | return this.pageNum; 37 | } 38 | 39 | /** 40 | * @return a hash code for this page, represented by the concatenation of 41 | * the table number and the page number (needed if a PageId is used as a 42 | * key in a hash table in the BufferPool, for example.) 43 | * @see BufferPool 44 | */ 45 | 46 | @Override 47 | public int hashCode() { 48 | int result = 31 * tableId + pageNum; 49 | return result; 50 | } 51 | 52 | /** 53 | * Compares one PageId to another. 54 | * 55 | * @param o The object to compare against (must be a PageId) 56 | * @return true if the objects are equal (e.g., page numbers and table 57 | * ids are the same) 58 | */ 59 | public boolean equals(Object o) { 60 | // some code goes here 61 | if (o == this) { 62 | return true; 63 | } 64 | if (o instanceof PageId) { 65 | PageId another = (PageId) o; 66 | return this.pageNum == another.getPageNumber() && this.tableId == another.getTableId(); 67 | } else 68 | return false; 69 | } 70 | 71 | /** 72 | * Return a representation of this object as an array of 73 | * integers, for writing to disk. Size of returned array must contain 74 | * number of integers that corresponds to number of args to one of the 75 | * constructors. 76 | */ 77 | public int[] serialize() { 78 | int data[] = new int[2]; 79 | 80 | data[0] = getTableId(); 81 | data[1] = getPageNumber(); 82 | 83 | return data; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/IntField.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.execution.Predicate; 4 | import simpledb.common.Type; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * Instance of Field that stores a single integer. 10 | */ 11 | public class IntField implements Field { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | private final int value; 16 | 17 | public int getValue() { 18 | return value; 19 | } 20 | 21 | /** 22 | * Constructor. 23 | * 24 | * @param i The value of this field. 25 | */ 26 | public IntField(int i) { 27 | value = i; 28 | } 29 | 30 | public String toString() { 31 | return Integer.toString(value); 32 | } 33 | 34 | public int hashCode() { 35 | return value; 36 | } 37 | 38 | public boolean equals(Object field) { 39 | if (!(field instanceof IntField)) 40 | return false; 41 | return ((IntField) field).value == value; 42 | } 43 | 44 | public void serialize(DataOutputStream dos) throws IOException { 45 | dos.writeInt(value); 46 | } 47 | 48 | /** 49 | * Compare the specified field to the value of this Field. 50 | * Return semantics are as specified by Field.compare 51 | * 52 | * @throws IllegalCastException if val is not an IntField 53 | * @see Field#compare 54 | */ 55 | public boolean compare(Predicate.Op op, Field val) { 56 | 57 | IntField iVal = (IntField) val; 58 | 59 | switch (op) { 60 | case EQUALS: 61 | case LIKE: 62 | return value == iVal.value; 63 | case NOT_EQUALS: 64 | return value != iVal.value; 65 | case GREATER_THAN: 66 | return value > iVal.value; 67 | case GREATER_THAN_OR_EQ: 68 | return value >= iVal.value; 69 | case LESS_THAN: 70 | return value < iVal.value; 71 | case LESS_THAN_OR_EQ: 72 | return value <= iVal.value; 73 | } 74 | 75 | return false; 76 | } 77 | 78 | /** 79 | * Return the Type of this field. 80 | * 81 | * @return Type.INT_TYPE 82 | */ 83 | public Type getType() { 84 | return Type.INT_TYPE; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/Page.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.transaction.TransactionId; 4 | 5 | /** 6 | * Page is the interface used to represent pages that are resident in the 7 | * BufferPool. Typically, DbFiles will read and write pages from disk. 8 | *

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

42 | * The invariant here is that it should be possible to pass the byte array 43 | * generated by getPageData to the Page constructor and have it produce 44 | * an identical Page object. 45 | * 46 | * @return A byte array correspond to the bytes of this page. 47 | */ 48 | 49 | byte[] getPageData(); 50 | 51 | /** Provide a representation of this page before any modifications were made 52 | to it. Used by recovery. 53 | */ 54 | Page getBeforeImage(); 55 | 56 | /* 57 | * a transaction that wrote this page just committed it. 58 | * copy current content to the before image. 59 | */ 60 | void setBeforeImage(); 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/PageId.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 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 | int[] serialize(); 13 | 14 | /** @return the unique tableid hashcode with this PageId */ 15 | 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 | 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 | boolean equals(Object o); 33 | 34 | int getPageNumber(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/RecordId.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 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 | private final PageId pageId; 14 | private final int tupleNum; 15 | 16 | /** 17 | * Creates a new RecordId referring to the specified PageId and tuple 18 | * number. 19 | * 20 | * @param pid 21 | * the pageid of the page on which the tuple resides 22 | * @param tupleno 23 | * the tuple number within the page. 24 | */ 25 | public RecordId(PageId pid, int tupleno) { 26 | // some code goes here 27 | this.pageId = pid; 28 | this.tupleNum = tupleno; 29 | } 30 | 31 | /** 32 | * @return the tuple number this RecordId references. 33 | */ 34 | public int getTupleNumber() { 35 | // some code goes here 36 | return this.tupleNum; 37 | } 38 | 39 | /** 40 | * @return the page id this RecordId references. 41 | */ 42 | public PageId getPageId() { 43 | // some code goes here 44 | return this.pageId; 45 | } 46 | 47 | /** 48 | * Two RecordId objects are considered equal if they represent the same 49 | * tuple. 50 | * 51 | * @return True if this and o represent the same tuple 52 | */ 53 | @Override 54 | public boolean equals(Object o) { 55 | // some code goes here 56 | if (o == this) { 57 | return true; 58 | } else if (o instanceof RecordId) { 59 | RecordId another = (RecordId) o; 60 | return pageId.equals(another.getPageId()) && another.getTupleNumber() == tupleNum; 61 | } else 62 | return false; 63 | } 64 | 65 | /** 66 | * You should implement the hashCode() so that two equal RecordId instances 67 | * (with respect to equals()) have the same hashCode(). 68 | * 69 | * @return An int that is the same for equal RecordId objects. 70 | */ 71 | @Override 72 | public int hashCode() { 73 | // some code goes here 74 | int result = 31 * pageId.hashCode() + tupleNum; 75 | return result; 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/StringField.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.common.Type; 4 | import simpledb.execution.Predicate; 5 | 6 | import java.io.*; 7 | 8 | /** 9 | * Instance of Field that stores a single String of a fixed length. 10 | */ 11 | public class StringField implements Field { 12 | 13 | private static final long serialVersionUID = 1L; 14 | 15 | private final String value; 16 | private final int maxSize; 17 | 18 | public String getValue() { 19 | return value; 20 | } 21 | 22 | /** 23 | * Constructor. 24 | * 25 | * @param s 26 | * The value of this field. 27 | * @param maxSize 28 | * The maximum size of this string 29 | */ 30 | public StringField(String s, int maxSize) { 31 | this.maxSize = maxSize; 32 | 33 | if (s.length() > maxSize) 34 | value = s.substring(0, maxSize); 35 | else 36 | value = s; 37 | } 38 | 39 | public String toString() { 40 | return value; 41 | } 42 | 43 | public int hashCode() { 44 | return value.hashCode(); 45 | } 46 | 47 | public boolean equals(Object field) { 48 | if (!(field instanceof StringField)) 49 | return false; 50 | return ((StringField) field).value.equals(value); 51 | } 52 | 53 | /** 54 | * Write this string to dos. Always writes maxSize + 4 bytes to the passed 55 | * in dos. First four bytes are string length, next bytes are string, with 56 | * remainder padded with 0 to maxSize. 57 | * 58 | * @param dos 59 | * Where the string is written 60 | */ 61 | public void serialize(DataOutputStream dos) throws IOException { 62 | String s = value; 63 | int overflow = maxSize - s.length(); 64 | if (overflow < 0) { 65 | s = s.substring(0, maxSize); 66 | } 67 | dos.writeInt(s.length()); 68 | dos.writeBytes(s); 69 | while (overflow-- > 0) 70 | dos.write((byte) 0); 71 | } 72 | 73 | /** 74 | * Compare the specified field to the value of this Field. Return semantics 75 | * are as specified by Field.compare 76 | * 77 | * @throws IllegalCastException 78 | * if val is not a StringField 79 | * @see Field#compare 80 | */ 81 | public boolean compare(Predicate.Op op, Field val) { 82 | 83 | StringField iVal = (StringField) val; 84 | int cmpVal = value.compareTo(iVal.value); 85 | 86 | switch (op) { 87 | case EQUALS: 88 | return cmpVal == 0; 89 | 90 | case NOT_EQUALS: 91 | return cmpVal != 0; 92 | 93 | case GREATER_THAN: 94 | return cmpVal > 0; 95 | 96 | case GREATER_THAN_OR_EQ: 97 | return cmpVal >= 0; 98 | 99 | case LESS_THAN: 100 | return cmpVal < 0; 101 | 102 | case LESS_THAN_OR_EQ: 103 | return cmpVal <= 0; 104 | 105 | case LIKE: 106 | return value.contains(iVal.value); 107 | } 108 | 109 | return false; 110 | } 111 | 112 | /** 113 | * @return the Type for this Field 114 | */ 115 | public Type getType() { 116 | 117 | return Type.STRING_TYPE; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/Tuple.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.util.IteratorWrapper; 4 | 5 | import java.io.Serializable; 6 | import java.util.Arrays; 7 | import java.util.Iterator; 8 | 9 | /** 10 | * Tuple maintains information about the contents of a tuple. Tuples have a 11 | * specified schema specified by a TupleDesc object and contain Field objects 12 | * with the data for each field. 13 | */ 14 | public class Tuple implements Serializable { 15 | 16 | private static final long serialVersionUID = 1L; 17 | 18 | private TupleDesc tupleDesc; 19 | private Field[] fields; 20 | private RecordId recordId; 21 | 22 | /** 23 | * Create a new tuple with the specified schema (type). 24 | * 25 | * @param td 26 | * the schema of this tuple. It must be a valid TupleDesc 27 | * instance with at least one field. 28 | */ 29 | public Tuple(TupleDesc td) { 30 | // some code goes here 31 | this.tupleDesc = td; 32 | this.fields = new Field[this.tupleDesc.numFields()]; 33 | } 34 | 35 | /** 36 | * @return The TupleDesc representing the schema of this tuple. 37 | */ 38 | public TupleDesc getTupleDesc() { 39 | // some code goes here 40 | return this.tupleDesc; 41 | } 42 | 43 | /** 44 | * @return The RecordId representing the location of this tuple on disk. May 45 | * be null. 46 | */ 47 | public RecordId getRecordId() { 48 | // some code goes here 49 | return this.recordId; 50 | } 51 | 52 | /** 53 | * Set the RecordId information for this tuple. 54 | * 55 | * @param rid 56 | * the new RecordId for this tuple. 57 | */ 58 | public void setRecordId(RecordId rid) { 59 | // some code goes here 60 | this.recordId = rid; 61 | } 62 | 63 | /** 64 | * Change the value of the ith field of this tuple. 65 | * 66 | * @param i 67 | * index of the field to change. It must be a valid index. 68 | * @param f 69 | * new value for the field. 70 | */ 71 | public void setField(int i, Field f) { 72 | // some code goes here 73 | if (i >= this.tupleDesc.numFields()) { 74 | return; 75 | } 76 | this.fields[i] = f; 77 | } 78 | 79 | /** 80 | * @return the value of the ith field, or null if it has not been set. 81 | * 82 | * @param i 83 | * field index to return. Must be a valid index. 84 | */ 85 | public Field getField(int i) { 86 | // some code goes here 87 | if (i >= this.tupleDesc.numFields()) { 88 | return null; 89 | } 90 | return this.fields[i]; 91 | } 92 | 93 | /** 94 | * Returns the contents of this Tuple as a string. Note that to pass the 95 | * system tests, the format needs to be as follows: 96 | * 97 | * column1\tcolumn2\tcolumn3\t...\tcolumnN 98 | * 99 | * where \t is any whitespace (except a newline) 100 | */ 101 | @Override 102 | public String toString() { 103 | return "Tuple{" + "tupleDesc=" + tupleDesc + ", fields=" + Arrays.toString(fields) + ", recordId=" + recordId 104 | + '}'; 105 | } 106 | 107 | /** 108 | * @return 109 | * An iterator which iterates over all the fields of this tuple 110 | * */ 111 | public Iterator fields() { 112 | // some code goes here 113 | 114 | return new IteratorWrapper<>(this.fields); 115 | } 116 | 117 | /** 118 | * reset the TupleDesc of this tuple (only affecting the TupleDesc) 119 | * */ 120 | public void resetTupleDesc(TupleDesc td) { 121 | // some code goes here 122 | this.tupleDesc = td; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/simpledb/storage/TupleIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.storage; 2 | 3 | import simpledb.execution.OpIterator; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * Implements a OpIterator by wrapping an Iterable. 9 | */ 10 | public class TupleIterator implements OpIterator { 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = 1L; 15 | Iterator i = null; 16 | TupleDesc td = null; 17 | Iterable tuples = null; 18 | 19 | /** 20 | * Constructs an iterator from the specified Iterable, and the specified 21 | * descriptor. 22 | * 23 | * @param tuples 24 | * The set of tuples to iterate over 25 | */ 26 | public TupleIterator(TupleDesc td, Iterable tuples) { 27 | this.td = td; 28 | this.tuples = tuples; 29 | 30 | // check that all tuples are the right TupleDesc 31 | for (Tuple t : tuples) { 32 | if (!t.getTupleDesc().equals(td)) 33 | throw new IllegalArgumentException("incompatible tuple in tuple set"); 34 | } 35 | } 36 | 37 | public void open() { 38 | i = tuples.iterator(); 39 | } 40 | 41 | public boolean hasNext() { 42 | return i.hasNext(); 43 | } 44 | 45 | public Tuple next() { 46 | return i.next(); 47 | } 48 | 49 | public void rewind() { 50 | close(); 51 | open(); 52 | } 53 | 54 | public TupleDesc getTupleDesc() { 55 | return td; 56 | } 57 | 58 | public void close() { 59 | i = null; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/simpledb/transaction/Lock.java: -------------------------------------------------------------------------------- 1 | package simpledb.transaction; 2 | 3 | public class Lock { 4 | private TransactionId tid; 5 | private int lockType; 6 | 7 | public Lock(final TransactionId tid, final int lockType) { 8 | this.tid = tid; 9 | this.lockType = lockType; 10 | } 11 | 12 | public int getLockType() { 13 | return lockType; 14 | } 15 | 16 | public void setLockType(final int lockType) { 17 | this.lockType = lockType; 18 | } 19 | 20 | public TransactionId getTid() { 21 | return tid; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/simpledb/transaction/LockManager.java: -------------------------------------------------------------------------------- 1 | package simpledb.transaction; 2 | 3 | import simpledb.storage.PageId; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | public class LockManager { 9 | private final Map> lockMap; 10 | 11 | public LockManager() { 12 | this.lockMap = new ConcurrentHashMap<>(); 13 | } 14 | 15 | public boolean tryAcquireLock(final PageId pageId, final TransactionId tid, final int lockType, final int timeout) { 16 | final long now = System.currentTimeMillis(); 17 | while (true) { 18 | if (System.currentTimeMillis() - now >= timeout) { 19 | return false; 20 | } 21 | if (acquireLock(pageId, tid, lockType)) { 22 | return true; 23 | } 24 | } 25 | 26 | } 27 | 28 | public synchronized boolean acquireLock(final PageId pageId, final TransactionId tid, final int lockType) { 29 | // 1.If page is unlock, just return true 30 | if (!this.lockMap.containsKey(pageId)) { 31 | final Lock lock = new Lock(tid, lockType); 32 | final List locks = new ArrayList<>(); 33 | locks.add(lock); 34 | this.lockMap.put(pageId, locks); 35 | return true; 36 | } 37 | final List locks = this.lockMap.get(pageId); 38 | 39 | // 2.Check whether this txn holds a lock 40 | for (final Lock lock : locks) { 41 | if (lock.getTid().equals(tid)) { 42 | if (lock.getLockType() == lockType) { 43 | return true; 44 | } 45 | if (lock.getLockType() == 1) { 46 | return true; 47 | } 48 | if (lock.getLockType() == 0 && locks.size() == 1) { 49 | lock.setLockType(1); 50 | return true; 51 | } 52 | return false; 53 | } 54 | } 55 | // 3.Check whether exists a writeLock 56 | if (locks.size() > 0 && locks.get(0).getLockType() == 1) { 57 | return false; 58 | } 59 | 60 | // 4.There already exists another locks, so we just can get a readLock 61 | if (lockType == 0) { 62 | final Lock lock = new Lock(tid, lockType); 63 | locks.add(lock); 64 | return true; 65 | } 66 | return false; 67 | } 68 | 69 | public synchronized boolean releaseLock(final PageId pageId, final TransactionId tid) { 70 | if (!this.lockMap.containsKey(pageId)) { 71 | return false; 72 | } 73 | final List locks = this.lockMap.get(pageId); 74 | for (int i = 0; i < locks.size(); i++) { 75 | final Lock lock = locks.get(i); 76 | if (lock.getTid().equals(tid)) { 77 | locks.remove(lock); 78 | this.lockMap.put(pageId, locks); 79 | if (locks.size() == 0) { 80 | this.lockMap.remove(pageId); 81 | } 82 | return true; 83 | } 84 | } 85 | return false; 86 | } 87 | 88 | public synchronized void releaseLockByTxn(final TransactionId tid) { 89 | this.lockMap.forEach((pid, locks) -> { 90 | if (holdsLock(pid, tid)) { 91 | releaseLock(pid, tid); 92 | } 93 | }); 94 | } 95 | 96 | public synchronized boolean holdsLock(final PageId pageId, final TransactionId tid) { 97 | if (!this.lockMap.containsKey(pageId)) { 98 | return false; 99 | } 100 | final List locks = this.lockMap.get(pageId); 101 | for (int i = 0; i < locks.size(); i++) { 102 | final Lock lock = locks.get(i); 103 | if (lock.getTid().equals(tid)) { 104 | return true; 105 | } 106 | } 107 | return false; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/simpledb/transaction/Transaction.java: -------------------------------------------------------------------------------- 1 | package simpledb.transaction; 2 | 3 | import simpledb.common.Database; 4 | 5 | import java.io.*; 6 | 7 | /** 8 | * Transaction encapsulates information about the state of 9 | * a transaction and manages transaction commit / abort. 10 | */ 11 | 12 | public class Transaction { 13 | private final TransactionId tid; 14 | volatile boolean started = false; 15 | 16 | public Transaction() { 17 | tid = new TransactionId(); 18 | } 19 | 20 | /** Start the transaction running */ 21 | public void start() { 22 | started = true; 23 | try { 24 | Database.getLogFile().logXactionBegin(tid); 25 | } catch (IOException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | public TransactionId getId() { 31 | return tid; 32 | } 33 | 34 | /** Finish the transaction */ 35 | public void commit() throws IOException { 36 | transactionComplete(false); 37 | } 38 | 39 | /** Finish the transaction */ 40 | public void abort() throws IOException { 41 | transactionComplete(true); 42 | } 43 | 44 | /** Handle the details of transaction commit / abort */ 45 | public void transactionComplete(boolean abort) throws IOException { 46 | 47 | if (started) { 48 | //write abort log record and rollback transaction 49 | if (abort) { 50 | Database.getLogFile().logAbort(tid); //does rollback too 51 | } 52 | 53 | // Release locks and flush pages if needed 54 | Database.getBufferPool().transactionComplete(tid, !abort); // release locks 55 | 56 | // write commit log record 57 | if (!abort) { 58 | Database.getLogFile().logCommit(tid); 59 | } 60 | 61 | //setting this here means we could possibly write multiple abort records -- OK? 62 | started = false; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/simpledb/transaction/TransactionAbortedException.java: -------------------------------------------------------------------------------- 1 | package simpledb.transaction; 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/main/java/simpledb/transaction/TransactionId.java: -------------------------------------------------------------------------------- 1 | package simpledb.transaction; 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 final 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 | return myid == other.myid; 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | final int prime = 31; 39 | int result = 1; 40 | result = prime * result + (int) (myid ^ (myid >>> 32)); 41 | return result; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/simpledb/util/HeapFileIterator.java: -------------------------------------------------------------------------------- 1 | package simpledb.util; 2 | 3 | import simpledb.common.Database; 4 | import simpledb.common.DbException; 5 | import simpledb.common.Permissions; 6 | import simpledb.storage.DbFileIterator; 7 | import simpledb.storage.HeapPage; 8 | import simpledb.storage.HeapPageId; 9 | import simpledb.storage.Tuple; 10 | import simpledb.transaction.TransactionAbortedException; 11 | import simpledb.transaction.TransactionId; 12 | 13 | import java.util.NoSuchElementException; 14 | 15 | public class HeapFileIterator implements DbFileIterator { 16 | private final int totalPage; 17 | private final TransactionId transactionId; 18 | private final int tableId; 19 | private int currentPageId; 20 | private PageCachePool pageCachePool; 21 | 22 | public HeapFileIterator(final int totalPages, final TransactionId transactionId, final int tableId) { 23 | this.totalPage = totalPages; 24 | this.transactionId = transactionId; 25 | this.tableId = tableId; 26 | } 27 | 28 | @Override 29 | public void open() throws DbException, TransactionAbortedException { 30 | this.pageCachePool = new PageCachePool(0.2, this.totalPage); 31 | this.currentPageId = 0; 32 | cacheFilePages(); 33 | } 34 | 35 | // Cache pages as many as have 36 | public void cacheFilePages() { 37 | this.pageCachePool.clear(); 38 | int i = this.currentPageId; 39 | for (; i < this.currentPageId + this.pageCachePool.getMaxCacheNum() && i < this.totalPage; i++) { 40 | try { 41 | final HeapPageId pageId = new HeapPageId(this.tableId, i); 42 | final HeapPage page = (HeapPage) Database.getBufferPool().getPage(this.transactionId, pageId, 43 | Permissions.READ_ONLY); 44 | this.pageCachePool.addPage(page.iterator()); 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | this.currentPageId = i; 50 | this.pageCachePool.init(); 51 | } 52 | 53 | @Override 54 | public boolean hasNext() throws DbException, TransactionAbortedException { 55 | if (this.pageCachePool == null) { 56 | return false; 57 | } 58 | if (this.pageCachePool.hasNext()) { 59 | return true; 60 | } 61 | // Cache another batch of pages 62 | while (!this.pageCachePool.hasNext()) { 63 | cacheFilePages(); 64 | if (this.totalPage == this.currentPageId) { 65 | break; 66 | } 67 | } 68 | return this.pageCachePool.hasNext(); 69 | } 70 | 71 | @Override 72 | public Tuple next() throws DbException, TransactionAbortedException, NoSuchElementException { 73 | if (hasNext()) { 74 | return this.pageCachePool.next(); 75 | } 76 | throw new NoSuchElementException("The Iterator don't have more elements"); 77 | } 78 | 79 | @Override 80 | public void rewind() throws DbException, TransactionAbortedException { 81 | open(); 82 | } 83 | 84 | @Override 85 | public void close() { 86 | if (this.pageCachePool != null) { 87 | this.pageCachePool.clear(); 88 | this.pageCachePool = null; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/simpledb/util/IteratorWrapper.java: -------------------------------------------------------------------------------- 1 | package simpledb.util; 2 | 3 | import java.util.Iterator; 4 | 5 | public class IteratorWrapper implements Iterator { 6 | private int currentPos; 7 | private T[] objects; 8 | 9 | public IteratorWrapper(final T[] objects) { 10 | this.objects = objects; 11 | this.currentPos = 0; 12 | } 13 | 14 | @Override 15 | public boolean hasNext() { 16 | return this.currentPos < this.objects.length && this.objects[this.currentPos] != null; 17 | } 18 | 19 | @Override 20 | public T next() { 21 | T value = this.objects[this.currentPos]; 22 | this.currentPos++; 23 | return value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/simpledb/util/LruCache.java: -------------------------------------------------------------------------------- 1 | package simpledb.util; 2 | 3 | import java.util.*; 4 | import java.util.stream.Collectors; 5 | 6 | public class LruCache { 7 | 8 | // LruCache node 9 | public class Node { 10 | public Node pre; 11 | public Node next; 12 | public K key; 13 | public V value; 14 | 15 | public Node(final K key, final V value) { 16 | this.key = key; 17 | this.value = value; 18 | } 19 | } 20 | 21 | private final int maxSize; 22 | private final Map nodeMap; 23 | private final Node head; 24 | private final Node tail; 25 | 26 | public LruCache(int maxSize) { 27 | this.maxSize = maxSize; 28 | this.head = new Node(null, null); 29 | this.tail = new Node(null, null); 30 | this.head.next = tail; 31 | this.tail.pre = head; 32 | this.nodeMap = new HashMap<>(); 33 | } 34 | 35 | public void linkToHead(Node node) { 36 | Node next = this.head.next; 37 | node.next = next; 38 | node.pre = this.head; 39 | 40 | this.head.next = node; 41 | next.pre = node; 42 | } 43 | 44 | public void moveToHead(Node node) { 45 | removeNode(node); 46 | linkToHead(node); 47 | } 48 | 49 | public void removeNode(Node node) { 50 | if (node.pre != null && node.next != null) { 51 | node.pre.next = node.next; 52 | node.next.pre = node.pre; 53 | } 54 | } 55 | 56 | public Node removeLast() { 57 | Node last = this.tail.pre; 58 | removeNode(last); 59 | return last; 60 | } 61 | 62 | public synchronized void remove(K key) { 63 | if (this.nodeMap.containsKey(key)) { 64 | final Node node = this.nodeMap.get(key); 65 | removeNode(node); 66 | this.nodeMap.remove(key); 67 | } 68 | } 69 | 70 | public synchronized V get(K key) { 71 | if (this.nodeMap.containsKey(key)) { 72 | Node node = this.nodeMap.get(key); 73 | moveToHead(node); 74 | return node.value; 75 | } 76 | return null; 77 | } 78 | 79 | // Return the evicted item if the space is insufficient 80 | public synchronized V put(K key, V value) { 81 | if (this.nodeMap.containsKey(key)) { 82 | Node node = this.nodeMap.get(key); 83 | node.value = value; 84 | moveToHead(node); 85 | } else { 86 | // if (this.nodeMap.size() == this.maxSize) { 87 | // Node last = removeLast(); 88 | // this.nodeMap.remove(last.key); 89 | // return last.value; 90 | // } 91 | Node node = new Node(key, value); 92 | this.nodeMap.put(key, node); 93 | linkToHead(node); 94 | } 95 | return null; 96 | } 97 | 98 | public synchronized Iterator reverseIterator() { 99 | Node last = this.tail.pre; 100 | final ArrayList list = new ArrayList<>(); 101 | while (!last.equals(this.head)) { 102 | list.add(last.value); 103 | last = last.pre; 104 | } 105 | return list.iterator(); 106 | } 107 | 108 | public synchronized Iterator valueIterator() { 109 | final Collection nodes = this.nodeMap.values(); 110 | final List valueList = nodes.stream().map(x -> x.value).collect(Collectors.toList()); 111 | return valueList.iterator(); 112 | } 113 | 114 | public synchronized int getSize() { 115 | return this.nodeMap.size(); 116 | } 117 | 118 | public int getMaxSize() { 119 | return maxSize; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/simpledb/util/PageCachePool.java: -------------------------------------------------------------------------------- 1 | package simpledb.util; 2 | 3 | import simpledb.storage.Tuple; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | // Cache pool for file pages(tuples), cache for fileIterator 10 | public class PageCachePool { 11 | private final List> pageCache; 12 | private Iterator currentIterator; 13 | private int currentIndex; 14 | private int maxCacheNum; 15 | 16 | public PageCachePool(final double cacheRate, final int totalPage) { 17 | this.maxCacheNum = (int) cacheRate * totalPage; 18 | if (this.maxCacheNum < 1) { 19 | this.maxCacheNum = 1; 20 | } 21 | this.pageCache = new ArrayList<>(); 22 | } 23 | 24 | public void addPage(final Iterator tupleIterator) { 25 | if (this.pageCache.size() < this.maxCacheNum) { 26 | this.pageCache.add(tupleIterator); 27 | } 28 | } 29 | 30 | public void init() { 31 | this.currentIndex = 0; 32 | if (this.pageCache.size() > 0) { 33 | this.currentIterator = this.pageCache.get(0); 34 | } 35 | } 36 | 37 | public void clear() { 38 | this.pageCache.clear(); 39 | this.currentIterator = null; 40 | } 41 | 42 | public boolean hasNext() { 43 | if (this.currentIterator == null || this.pageCache.size() == 0) { 44 | return false; 45 | } 46 | if (this.currentIterator.hasNext()) { 47 | return true; 48 | } 49 | if (this.currentIndex + 1 < this.pageCache.size()) { 50 | this.currentIndex++; 51 | this.currentIterator = this.pageCache.get(this.currentIndex); 52 | return this.currentIterator.hasNext(); 53 | } 54 | return false; 55 | } 56 | 57 | public Tuple next() { 58 | if (hasNext()) { 59 | return this.currentIterator.next(); 60 | } 61 | return null; 62 | } 63 | 64 | public int getMaxCacheNum() { 65 | return maxCacheNum; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/test/simpledb/BTreePageIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import junit.framework.JUnit4TestAdapter; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import simpledb.index.BTreePageId; 9 | import simpledb.systemtest.SimpleDbTestBase; 10 | 11 | import static org.junit.Assert.*; 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 | assertNotEquals(null, pid1); 83 | 84 | // .equals() with the wrong type should return false 85 | assertNotEquals(pid1, new Object()); 86 | 87 | assertEquals(pid1, pid1); 88 | assertEquals(pid1, pid1Copy); 89 | assertEquals(pid1Copy, pid1); 90 | assertEquals(pid2, pid2); 91 | assertEquals(pid3, pid3); 92 | 93 | assertNotEquals(pid1, pid2); 94 | assertNotEquals(pid1Copy, pid2); 95 | assertNotEquals(pid2, pid1); 96 | assertNotEquals(pid2, pid1Copy); 97 | assertNotEquals(pid1, pid3); 98 | assertNotEquals(pid3, 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 | -------------------------------------------------------------------------------- /src/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 org.junit.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.common.Database; 16 | import simpledb.common.Utility; 17 | import simpledb.storage.DbFile; 18 | import simpledb.storage.TupleDesc; 19 | import simpledb.systemtest.SimpleDbTestBase; 20 | import simpledb.systemtest.SystemTestUtil; 21 | 22 | public class CatalogTest extends SimpleDbTestBase { 23 | private static final Random r = new Random(); 24 | private static final String name = SystemTestUtil.getUUID(); 25 | private static final int id1 = r.nextInt(); 26 | private static final int id2 = r.nextInt(); 27 | private String nameThisTestRun; 28 | 29 | @Before public void addTables() { 30 | Database.getCatalog().clear(); 31 | nameThisTestRun = SystemTestUtil.getUUID(); 32 | Database.getCatalog().addTable(new SkeletonFile(id1, Utility.getTupleDesc(2)), nameThisTestRun); 33 | Database.getCatalog().addTable(new SkeletonFile(id2, Utility.getTupleDesc(2)), name); 34 | } 35 | 36 | /** 37 | * Unit test for Catalog.getTupleDesc() 38 | */ 39 | @Test public void getTupleDesc() { 40 | TupleDesc expected = Utility.getTupleDesc(2); 41 | TupleDesc actual = Database.getCatalog().getTupleDesc(id1); 42 | 43 | assertEquals(expected, actual); 44 | } 45 | 46 | /** 47 | * Unit test for Catalog.getTableId() 48 | */ 49 | @Test public void getTableId() { 50 | assertEquals(id2, Database.getCatalog().getTableId(name)); 51 | assertEquals(id1, Database.getCatalog().getTableId(nameThisTestRun)); 52 | 53 | try { 54 | Database.getCatalog().getTableId(null); 55 | Assert.fail("Should not find table with null name"); 56 | } catch (NoSuchElementException e) { 57 | // Expected to get here 58 | } 59 | 60 | try { 61 | Database.getCatalog().getTableId("foo"); 62 | Assert.fail("Should not find table with name foo"); 63 | } catch (NoSuchElementException e) { 64 | // Expected to get here 65 | } 66 | } 67 | 68 | /** 69 | * Unit test for Catalog.getDatabaseFile() 70 | */ 71 | 72 | @Test public void getDatabaseFile() { 73 | DbFile f = Database.getCatalog().getDatabaseFile(id1); 74 | 75 | // NOTE(ghuo): we try not to dig too deeply into the DbFile API here; we 76 | // rely on HeapFileTest for that. perform some basic checks. 77 | assertEquals(id1, f.getId()); 78 | } 79 | 80 | /** 81 | * Check that duplicate names are handled correctly 82 | */ 83 | @Test public void handleDuplicateNames() { 84 | int id3 = r.nextInt(); 85 | Database.getCatalog().addTable(new SkeletonFile(id3, Utility.getTupleDesc(2)), name); 86 | assertEquals(id3, Database.getCatalog().getTableId(name)); 87 | } 88 | 89 | /** 90 | * Check that duplicate file ids are handled correctly 91 | */ 92 | @Test public void handleDuplicateIds() { 93 | String newName = SystemTestUtil.getUUID(); 94 | DbFile f = new SkeletonFile(id2, Utility.getTupleDesc(2)); 95 | Database.getCatalog().addTable(f, newName); 96 | assertEquals(newName, Database.getCatalog().getTableName(id2)); 97 | assertEquals(f, Database.getCatalog().getDatabaseFile(id2)); 98 | } 99 | 100 | /** 101 | * JUnit suite target 102 | */ 103 | public static junit.framework.Test suite() { 104 | return new JUnit4TestAdapter(CatalogTest.class); 105 | } 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/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.common.Utility; 12 | import simpledb.execution.Filter; 13 | import simpledb.execution.OpIterator; 14 | import simpledb.execution.Predicate; 15 | import simpledb.storage.Tuple; 16 | import simpledb.storage.TupleDesc; 17 | import simpledb.systemtest.SimpleDbTestBase; 18 | 19 | public class FilterTest extends SimpleDbTestBase { 20 | 21 | final int testWidth = 3; 22 | OpIterator scan; 23 | 24 | /** 25 | * Initialize each unit test 26 | */ 27 | @Before public void setUp() { 28 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 29 | } 30 | 31 | /** 32 | * Unit test for Filter.getTupleDesc() 33 | */ 34 | @Test public void getTupleDesc() { 35 | Predicate pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 36 | Filter op = new Filter(pred, scan); 37 | TupleDesc expected = Utility.getTupleDesc(testWidth); 38 | TupleDesc actual = op.getTupleDesc(); 39 | assertEquals(expected, actual); 40 | } 41 | 42 | /** 43 | * Unit test for Filter.rewind() 44 | */ 45 | @Test public void rewind() throws Exception { 46 | Predicate pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 47 | Filter op = new Filter(pred, scan); 48 | op.open(); 49 | assertTrue(op.hasNext()); 50 | assertNotNull(op.next()); 51 | assertTrue(TestUtil.checkExhausted(op)); 52 | 53 | op.rewind(); 54 | Tuple expected = Utility.getHeapTuple(0, testWidth); 55 | Tuple actual = op.next(); 56 | assertTrue(TestUtil.compareTuples(expected, actual)); 57 | op.close(); 58 | } 59 | 60 | /** 61 | * Unit test for Filter.getNext() using a < predicate that filters 62 | * some tuples 63 | */ 64 | @Test public void filterSomeLessThan() throws Exception { 65 | Predicate pred; 66 | pred = new Predicate(0, Predicate.Op.LESS_THAN, TestUtil.getField(2)); 67 | Filter op = new Filter(pred, scan); 68 | TestUtil.MockScan expectedOut = new TestUtil.MockScan(-5, 2, testWidth); 69 | op.open(); 70 | TestUtil.compareDbIterators(op, expectedOut); 71 | op.close(); 72 | } 73 | 74 | /** 75 | * Unit test for Filter.getNext() using a < predicate that filters 76 | * everything 77 | */ 78 | @Test public void filterAllLessThan() throws Exception { 79 | Predicate pred; 80 | pred = new Predicate(0, Predicate.Op.LESS_THAN, TestUtil.getField(-5)); 81 | Filter op = new Filter(pred, scan); 82 | op.open(); 83 | assertTrue(TestUtil.checkExhausted(op)); 84 | op.close(); 85 | } 86 | 87 | /** 88 | * Unit test for Filter.getNext() using an = predicate 89 | */ 90 | @Test public void filterEqual() throws Exception { 91 | Predicate pred; 92 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 93 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(-5)); 94 | Filter op = new Filter(pred, scan); 95 | op.open(); 96 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(-5, testWidth), 97 | op.next())); 98 | op.close(); 99 | 100 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 101 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(0)); 102 | op = new Filter(pred, scan); 103 | op.open(); 104 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(0, testWidth), 105 | op.next())); 106 | op.close(); 107 | 108 | this.scan = new TestUtil.MockScan(-5, 5, testWidth); 109 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(4)); 110 | op = new Filter(pred, scan); 111 | op.open(); 112 | assertTrue(TestUtil.compareTuples(Utility.getHeapTuple(4, testWidth), 113 | op.next())); 114 | op.close(); 115 | } 116 | 117 | /** 118 | * Unit test for Filter.getNext() using an = predicate passing no tuples 119 | */ 120 | @Test public void filterEqualNoTuples() throws Exception { 121 | Predicate pred; 122 | pred = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(5)); 123 | Filter op = new Filter(pred, scan); 124 | op.open(); 125 | TestUtil.checkExhausted(op); 126 | op.close(); 127 | } 128 | 129 | /** 130 | * JUnit suite target 131 | */ 132 | public static junit.framework.Test suite() { 133 | return new JUnit4TestAdapter(FilterTest.class); 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /src/test/simpledb/HeapFileReadTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.common.Database; 4 | import simpledb.common.Utility; 5 | import simpledb.storage.*; 6 | import simpledb.systemtest.SimpleDbTestBase; 7 | import simpledb.systemtest.SystemTestUtil; 8 | 9 | import java.util.*; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | 14 | import static org.junit.Assert.*; 15 | import junit.framework.JUnit4TestAdapter; 16 | import simpledb.transaction.TransactionId; 17 | 18 | public class HeapFileReadTest extends SimpleDbTestBase { 19 | private HeapFile hf; 20 | private TransactionId tid; 21 | private TupleDesc td; 22 | 23 | /** 24 | * Set up initial resources for each unit test. 25 | */ 26 | @Before 27 | public void setUp() throws Exception { 28 | hf = SystemTestUtil.createRandomHeapFile(2, 20, null, null); 29 | td = Utility.getTupleDesc(2); 30 | tid = new TransactionId(); 31 | } 32 | 33 | @After 34 | public void tearDown() { 35 | Database.getBufferPool().transactionComplete(tid); 36 | } 37 | 38 | /** 39 | * Unit test for HeapFile.getId() 40 | */ 41 | @Test 42 | public void getId() throws Exception { 43 | int id = hf.getId(); 44 | 45 | // NOTE(ghuo): the value could be anything. test determinism, at least. 46 | assertEquals(id, hf.getId()); 47 | assertEquals(id, hf.getId()); 48 | 49 | HeapFile other = SystemTestUtil.createRandomHeapFile(1, 1, null, null); 50 | assertTrue(id != other.getId()); 51 | } 52 | 53 | /** 54 | * Unit test for HeapFile.getTupleDesc() 55 | */ 56 | @Test 57 | public void getTupleDesc() { 58 | assertEquals(td, hf.getTupleDesc()); 59 | } 60 | /** 61 | * Unit test for HeapFile.numPages() 62 | */ 63 | @Test 64 | public void numPages() { 65 | assertEquals(1, hf.numPages()); 66 | // assertEquals(1, empty.numPages()); 67 | } 68 | 69 | /** 70 | * Unit test for HeapFile.readPage() 71 | */ 72 | @Test 73 | public void readPage() { 74 | HeapPageId pid = new HeapPageId(hf.getId(), 0); 75 | HeapPage page = (HeapPage) hf.readPage(pid); 76 | 77 | // NOTE(ghuo): we try not to dig too deeply into the Page API here; we 78 | // rely on HeapPageTest for that. perform some basic checks. 79 | assertEquals(484, page.getNumEmptySlots()); 80 | assertTrue(page.isSlotUsed(1)); 81 | assertFalse(page.isSlotUsed(20)); 82 | } 83 | 84 | @Test 85 | public void testIteratorBasic() throws Exception { 86 | HeapFile smallFile = SystemTestUtil.createRandomHeapFile(2, 3, null, 87 | null); 88 | 89 | DbFileIterator it = smallFile.iterator(tid); 90 | // Not open yet 91 | assertFalse(it.hasNext()); 92 | try { 93 | it.next(); 94 | fail("expected exception"); 95 | } catch (NoSuchElementException ignored) { 96 | } 97 | 98 | it.open(); 99 | int count = 0; 100 | while (it.hasNext()) { 101 | assertNotNull(it.next()); 102 | count += 1; 103 | } 104 | assertEquals(3, count); 105 | it.close(); 106 | } 107 | 108 | @Test 109 | public void testIteratorClose() throws Exception { 110 | // make more than 1 page. Previous closed iterator would start fetching 111 | // from page 1. 112 | HeapFile twoPageFile = SystemTestUtil.createRandomHeapFile(2, 520, 113 | null, null); 114 | 115 | DbFileIterator it = twoPageFile.iterator(tid); 116 | it.open(); 117 | assertTrue(it.hasNext()); 118 | it.close(); 119 | try { 120 | it.next(); 121 | fail("expected exception"); 122 | } catch (NoSuchElementException ignored) { 123 | } 124 | // close twice is harmless 125 | it.close(); 126 | } 127 | 128 | /** 129 | * JUnit suite target 130 | */ 131 | public static junit.framework.Test suite() { 132 | return new JUnit4TestAdapter(HeapFileReadTest.class); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/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 | import simpledb.common.Database; 10 | import simpledb.common.Utility; 11 | import simpledb.storage.*; 12 | import simpledb.systemtest.SystemTestUtil; 13 | import simpledb.transaction.TransactionId; 14 | 15 | import java.util.Arrays; 16 | 17 | public class HeapFileWriteTest extends TestUtil.CreateHeapFile { 18 | private TransactionId tid; 19 | 20 | /** 21 | * Set up initial resources for each unit test. 22 | */ 23 | @Before public void setUp() throws Exception { 24 | super.setUp(); 25 | tid = new TransactionId(); 26 | } 27 | 28 | @After public void tearDown() { 29 | Database.getBufferPool().transactionComplete(tid); 30 | } 31 | 32 | /** 33 | * Unit test for HeapFile.addTuple() 34 | */ 35 | @Test public void addTuple() throws Exception { 36 | // we should be able to add 504 tuples on an empty page. 37 | for (int i = 0; i < 504; ++i) { 38 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 39 | assertEquals(1, empty.numPages()); 40 | } 41 | 42 | // the next 512 additions should live on a new page 43 | for (int i = 0; i < 504; ++i) { 44 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 45 | assertEquals(2, empty.numPages()); 46 | } 47 | 48 | // and one more, just for fun... 49 | empty.insertTuple(tid, Utility.getHeapTuple(0, 2)); 50 | assertEquals(3, empty.numPages()); 51 | } 52 | 53 | @Test 54 | public void testAlternateEmptyAndFullPagesThenIterate() throws Exception { 55 | // Create HeapFile/Table 56 | HeapFile smallFile = SystemTestUtil.createRandomHeapFile(2, 3, null, 57 | null); 58 | // Grab table id 59 | int tableId = smallFile.getId(); 60 | int tdSize = 8; 61 | int numTuples = (BufferPool.getPageSize()*8) / (tdSize * 8 + 1); 62 | int headerSize = (int) Math.ceil(numTuples / 8.0); 63 | // Leave these as all zeroes so this entire page is empty 64 | byte[] empty = new byte[numTuples * 8 + headerSize]; 65 | byte[] full = new byte[numTuples * 8 + headerSize]; 66 | // Since every bit is marked as used, every tuple should be used, 67 | // and all should be set to -1. 68 | Arrays.fill(full, (byte) 0xFFFFFFFF); 69 | 70 | // The first two pages and the fourth page are empty and should be skipped 71 | // while still continuing on to read the third and fifth page. 72 | // Hint: You can see this in HeapFile's iterator right after assigning the 73 | // next HeapPage's iterator and checking if it's empty (hasNext()), making 74 | // sure it moves onto the next page until hitting the final page. 75 | smallFile.writePage(new HeapPage(new HeapPageId(tableId, 0), empty)); 76 | smallFile.writePage(new HeapPage(new HeapPageId(tableId, 1), empty)); 77 | smallFile.writePage(new HeapPage(new HeapPageId(tableId, 2), full)); 78 | smallFile.writePage(new HeapPage(new HeapPageId(tableId, 3), empty)); 79 | smallFile.writePage(new HeapPage(new HeapPageId(tableId, 4), full)); 80 | DbFileIterator it = smallFile.iterator(tid); 81 | it.open(); 82 | int count = 0; 83 | while (it.hasNext()) { 84 | Tuple t = it.next(); 85 | assertNotNull(t); 86 | count += 1; 87 | } 88 | // Since we have two full pages, we should see all of 2*numTuples. 89 | assertEquals(2*numTuples, count); 90 | it.close(); 91 | } 92 | 93 | /** 94 | * JUnit suite target 95 | */ 96 | public static junit.framework.Test suite() { 97 | return new JUnit4TestAdapter(HeapFileWriteTest.class); 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/test/simpledb/HeapPageIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import junit.framework.JUnit4TestAdapter; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import simpledb.storage.HeapPageId; 9 | import simpledb.systemtest.SimpleDbTestBase; 10 | 11 | import static org.junit.Assert.*; 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 | assertNotEquals(null, pid1); 64 | 65 | // .equals() with the wrong type should return false 66 | assertNotEquals(pid1, new Object()); 67 | 68 | assertEquals(pid1, pid1); 69 | assertEquals(pid1, pid1Copy); 70 | assertEquals(pid1Copy, pid1); 71 | assertEquals(pid2, pid2); 72 | 73 | assertNotEquals(pid1, pid2); 74 | assertNotEquals(pid1Copy, pid2); 75 | assertNotEquals(pid2, pid1); 76 | assertNotEquals(pid2, 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 | -------------------------------------------------------------------------------- /src/test/simpledb/HeapPageReadTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import simpledb.TestUtil.SkeletonFile; 4 | import simpledb.common.Database; 5 | import simpledb.common.Utility; 6 | import simpledb.storage.*; 7 | import simpledb.systemtest.SimpleDbTestBase; 8 | import simpledb.systemtest.SystemTestUtil; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.util.*; 13 | 14 | import org.junit.Before; 15 | import org.junit.Test; 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertTrue; 19 | import junit.framework.JUnit4TestAdapter; 20 | 21 | public class HeapPageReadTest extends SimpleDbTestBase { 22 | private HeapPageId pid; 23 | 24 | public static final int[][] EXAMPLE_VALUES = new int[][] { 25 | { 31933, 862 }, 26 | { 29402, 56883 }, 27 | { 1468, 5825 }, 28 | { 17876, 52278 }, 29 | { 6350, 36090 }, 30 | { 34784, 43771 }, 31 | { 28617, 56874 }, 32 | { 19209, 23253 }, 33 | { 56462, 24979 }, 34 | { 51440, 56685 }, 35 | { 3596, 62307 }, 36 | { 45569, 2719 }, 37 | { 22064, 43575 }, 38 | { 42812, 44947 }, 39 | { 22189, 19724 }, 40 | { 33549, 36554 }, 41 | { 9086, 53184 }, 42 | { 42878, 33394 }, 43 | { 62778, 21122 }, 44 | { 17197, 16388 } 45 | }; 46 | 47 | public static final byte[] EXAMPLE_DATA; 48 | static { 49 | // Build the input table 50 | List> table = new ArrayList<>(); 51 | for (int[] tuple : EXAMPLE_VALUES) { 52 | List listTuple = new ArrayList<>(); 53 | for (int value : tuple) { 54 | listTuple.add(value); 55 | } 56 | table.add(listTuple); 57 | } 58 | 59 | // Convert it to a HeapFile and read in the bytes 60 | try { 61 | File temp = File.createTempFile("table", ".dat"); 62 | temp.deleteOnExit(); 63 | HeapFileEncoder.convert(table, temp, BufferPool.getPageSize(), 2); 64 | EXAMPLE_DATA = TestUtil.readFileBytes(temp.getAbsolutePath()); 65 | } catch (IOException e) { 66 | throw new RuntimeException(e); 67 | } 68 | } 69 | 70 | /** 71 | * Set up initial resources for each unit test. 72 | */ 73 | @Before public void addTable() { 74 | this.pid = new HeapPageId(-1, -1); 75 | Database.getCatalog().addTable(new SkeletonFile(-1, Utility.getTupleDesc(2)), SystemTestUtil.getUUID()); 76 | } 77 | 78 | /** 79 | * Unit test for HeapPage.getId() 80 | */ 81 | @Test public void getId() throws Exception { 82 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 83 | assertEquals(pid, page.getId()); 84 | } 85 | 86 | /** 87 | * Unit test for HeapPage.iterator() 88 | */ 89 | @Test public void testIterator() throws Exception { 90 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 91 | Iterator it = page.iterator(); 92 | 93 | int row = 0; 94 | while (it.hasNext()) { 95 | Tuple tup = it.next(); 96 | IntField f0 = (IntField) tup.getField(0); 97 | IntField f1 = (IntField) tup.getField(1); 98 | 99 | assertEquals(EXAMPLE_VALUES[row][0], f0.getValue()); 100 | assertEquals(EXAMPLE_VALUES[row][1], f1.getValue()); 101 | row++; 102 | } 103 | } 104 | 105 | /** 106 | * Unit test for HeapPage.getNumEmptySlots() 107 | */ 108 | @Test public void getNumEmptySlots() throws Exception { 109 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 110 | assertEquals(484, page.getNumEmptySlots()); 111 | } 112 | 113 | /** 114 | * Unit test for HeapPage.isSlotUsed() 115 | */ 116 | @Test public void getSlot() throws Exception { 117 | HeapPage page = new HeapPage(pid, EXAMPLE_DATA); 118 | 119 | for (int i = 0; i < 20; ++i) 120 | assertTrue(page.isSlotUsed(i)); 121 | 122 | for (int i = 20; i < 504; ++i) 123 | assertFalse(page.isSlotUsed(i)); 124 | } 125 | 126 | /** 127 | * JUnit suite target 128 | */ 129 | public static junit.framework.Test suite() { 130 | return new JUnit4TestAdapter(HeapPageReadTest.class); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/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 | import simpledb.common.Utility; 10 | import simpledb.execution.Insert; 11 | import simpledb.execution.OpIterator; 12 | import simpledb.storage.TupleDesc; 13 | import simpledb.transaction.TransactionId; 14 | 15 | /** 16 | * We reserve more heavy-duty insertion testing for HeapFile and HeapPage. 17 | * This suite is superficial. 18 | */ 19 | public class InsertTest extends TestUtil.CreateHeapFile { 20 | 21 | private OpIterator scan1; 22 | private TransactionId tid; 23 | 24 | /** 25 | * Initialize each unit test 26 | */ 27 | @Before public void setUp() throws Exception { 28 | super.setUp(); 29 | this.scan1 = TestUtil.createTupleList(2, 30 | new int[] { 1, 2, 31 | 1, 4, 32 | 1, 6, 33 | 3, 2, 34 | 3, 4, 35 | 3, 6, 36 | 5, 7 }); 37 | tid = new TransactionId(); 38 | } 39 | 40 | /** 41 | * Unit test for Insert.getTupleDesc() 42 | */ 43 | @Test public void getTupleDesc() throws Exception { 44 | Insert op = new Insert(tid,scan1, empty.getId()); 45 | TupleDesc expected = Utility.getTupleDesc(1); 46 | TupleDesc actual = op.getTupleDesc(); 47 | assertEquals(expected, actual); 48 | } 49 | 50 | /** 51 | * Unit test for Insert.getNext(), inserting elements into an empty file 52 | */ 53 | @Test public void getNext() throws Exception { 54 | Insert op = new Insert(tid,scan1, empty.getId()); 55 | op.open(); 56 | assertTrue(TestUtil.compareTuples( 57 | Utility.getHeapTuple(7, 1), // the length of scan1 58 | op.next())); 59 | 60 | // we should fit on one page 61 | assertEquals(1, empty.numPages()); 62 | } 63 | 64 | /** 65 | * JUnit suite target 66 | */ 67 | public static junit.framework.Test suite() { 68 | return new JUnit4TestAdapter(InsertTest.class); 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /src/test/simpledb/JoinPredicateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Test; 4 | 5 | import simpledb.common.Utility; 6 | import simpledb.execution.JoinPredicate; 7 | import simpledb.execution.Predicate; 8 | import simpledb.systemtest.SimpleDbTestBase; 9 | import static org.junit.Assert.assertTrue; 10 | import static org.junit.Assert.assertFalse; 11 | import junit.framework.JUnit4TestAdapter; 12 | 13 | public class JoinPredicateTest extends SimpleDbTestBase { 14 | 15 | /** 16 | * Unit test for JoinPredicate.filter() 17 | */ 18 | @Test public void filterVaryingVals() { 19 | int[] vals = new int[] { -1, 0, 1 }; 20 | 21 | for (int i : vals) { 22 | JoinPredicate p = new JoinPredicate(0, 23 | Predicate.Op.EQUALS, 0); 24 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 25 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 26 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 27 | } 28 | 29 | for (int i : vals) { 30 | JoinPredicate p = new JoinPredicate(0, 31 | Predicate.Op.GREATER_THAN, 0); 32 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 33 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 34 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 35 | } 36 | 37 | for (int i : vals) { 38 | JoinPredicate p = new JoinPredicate(0, 39 | Predicate.Op.GREATER_THAN_OR_EQ, 0); 40 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 41 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 42 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 43 | } 44 | 45 | for (int i : vals) { 46 | JoinPredicate p = new JoinPredicate(0, 47 | Predicate.Op.LESS_THAN, 0); 48 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 49 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 50 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 51 | } 52 | 53 | for (int i : vals) { 54 | JoinPredicate p = new JoinPredicate(0, 55 | Predicate.Op.LESS_THAN_OR_EQ, 0); 56 | assertFalse(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i - 1))); 57 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i))); 58 | assertTrue(p.filter(Utility.getHeapTuple(i), Utility.getHeapTuple(i + 1))); 59 | } 60 | } 61 | 62 | /** 63 | * JUnit suite target 64 | */ 65 | public static junit.framework.Test suite() { 66 | return new JUnit4TestAdapter(JoinPredicateTest.class); 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/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.common.Utility; 12 | import simpledb.execution.Join; 13 | import simpledb.execution.JoinPredicate; 14 | import simpledb.execution.OpIterator; 15 | import simpledb.execution.Predicate; 16 | import simpledb.storage.Tuple; 17 | import simpledb.storage.TupleDesc; 18 | import simpledb.systemtest.SimpleDbTestBase; 19 | 20 | public class JoinTest extends SimpleDbTestBase { 21 | 22 | final int width1 = 2; 23 | final int width2 = 3; 24 | OpIterator scan1; 25 | OpIterator scan2; 26 | OpIterator eqJoin; 27 | OpIterator gtJoin; 28 | 29 | /** 30 | * Initialize each unit test 31 | */ 32 | @Before public void createTupleLists() { 33 | this.scan1 = TestUtil.createTupleList(width1, 34 | new int[] { 1, 2, 35 | 3, 4, 36 | 5, 6, 37 | 7, 8 }); 38 | this.scan2 = TestUtil.createTupleList(width2, 39 | new int[] { 1, 2, 3, 40 | 2, 3, 4, 41 | 3, 4, 5, 42 | 4, 5, 6, 43 | 5, 6, 7 }); 44 | this.eqJoin = TestUtil.createTupleList(width1 + width2, 45 | new int[] { 1, 2, 1, 2, 3, 46 | 3, 4, 3, 4, 5, 47 | 5, 6, 5, 6, 7 }); 48 | this.gtJoin = TestUtil.createTupleList(width1 + width2, 49 | new int[] { 50 | 3, 4, 1, 2, 3, // 1, 2 < 3 51 | 3, 4, 2, 3, 4, 52 | 5, 6, 1, 2, 3, // 1, 2, 3, 4 < 5 53 | 5, 6, 2, 3, 4, 54 | 5, 6, 3, 4, 5, 55 | 5, 6, 4, 5, 6, 56 | 7, 8, 1, 2, 3, // 1, 2, 3, 4, 5 < 7 57 | 7, 8, 2, 3, 4, 58 | 7, 8, 3, 4, 5, 59 | 7, 8, 4, 5, 6, 60 | 7, 8, 5, 6, 7 }); 61 | } 62 | 63 | /** 64 | * Unit test for Join.getTupleDesc() 65 | */ 66 | @Test public void getTupleDesc() { 67 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 68 | Join op = new Join(pred, scan1, scan2); 69 | TupleDesc expected = Utility.getTupleDesc(width1 + width2); 70 | TupleDesc actual = op.getTupleDesc(); 71 | assertEquals(expected, actual); 72 | } 73 | 74 | /** 75 | * Unit test for Join.rewind() 76 | */ 77 | @Test public void rewind() throws Exception { 78 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 79 | Join op = new Join(pred, scan1, scan2); 80 | op.open(); 81 | while (op.hasNext()) { 82 | assertNotNull(op.next()); 83 | } 84 | assertTrue(TestUtil.checkExhausted(op)); 85 | op.rewind(); 86 | 87 | eqJoin.open(); 88 | Tuple expected = eqJoin.next(); 89 | Tuple actual = op.next(); 90 | assertTrue(TestUtil.compareTuples(expected, actual)); 91 | } 92 | 93 | /** 94 | * Unit test for Join.getNext() using a > predicate 95 | */ 96 | @Test public void gtJoin() throws Exception { 97 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.GREATER_THAN, 0); 98 | Join op = new Join(pred, scan1, scan2); 99 | op.open(); 100 | gtJoin.open(); 101 | TestUtil.matchAllTuples(gtJoin, op); 102 | } 103 | 104 | /** 105 | * Unit test for Join.getNext() using an = predicate 106 | */ 107 | @Test public void eqJoin() throws Exception { 108 | JoinPredicate pred = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 109 | Join op = new Join(pred, scan1, scan2); 110 | op.open(); 111 | eqJoin.open(); 112 | TestUtil.matchAllTuples(eqJoin, op); 113 | } 114 | 115 | /** 116 | * JUnit suite target 117 | */ 118 | public static junit.framework.Test suite() { 119 | return new JUnit4TestAdapter(JoinTest.class); 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/test/simpledb/PredicateTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Test; 4 | 5 | import simpledb.common.Utility; 6 | import simpledb.execution.Predicate; 7 | import simpledb.systemtest.SimpleDbTestBase; 8 | import static org.junit.Assert.assertTrue; 9 | import static org.junit.Assert.assertFalse; 10 | import junit.framework.JUnit4TestAdapter; 11 | 12 | public class PredicateTest extends SimpleDbTestBase{ 13 | 14 | /** 15 | * Unit test for Predicate.filter() 16 | */ 17 | @Test public void filter() { 18 | int[] vals = new int[] { -1, 0, 1 }; 19 | 20 | for (int i : vals) { 21 | Predicate p = new Predicate(0, Predicate.Op.EQUALS, TestUtil.getField(i)); 22 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 23 | assertTrue(p.filter(Utility.getHeapTuple(i))); 24 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 25 | } 26 | 27 | for (int i : vals) { 28 | Predicate p = new Predicate(0, Predicate.Op.GREATER_THAN, 29 | TestUtil.getField(i)); 30 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 31 | assertFalse(p.filter(Utility.getHeapTuple(i))); 32 | assertTrue(p.filter(Utility.getHeapTuple(i + 1))); 33 | } 34 | 35 | for (int i : vals) { 36 | Predicate p = new Predicate(0, Predicate.Op.GREATER_THAN_OR_EQ, 37 | TestUtil.getField(i)); 38 | assertFalse(p.filter(Utility.getHeapTuple(i - 1))); 39 | assertTrue(p.filter(Utility.getHeapTuple(i))); 40 | assertTrue(p.filter(Utility.getHeapTuple(i + 1))); 41 | } 42 | 43 | for (int i : vals) { 44 | Predicate p = new Predicate(0, Predicate.Op.LESS_THAN, 45 | TestUtil.getField(i)); 46 | assertTrue(p.filter(Utility.getHeapTuple(i - 1))); 47 | assertFalse(p.filter(Utility.getHeapTuple(i))); 48 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 49 | } 50 | 51 | for (int i : vals) { 52 | Predicate p = new Predicate(0, Predicate.Op.LESS_THAN_OR_EQ, 53 | TestUtil.getField(i)); 54 | assertTrue(p.filter(Utility.getHeapTuple(i - 1))); 55 | assertTrue(p.filter(Utility.getHeapTuple(i))); 56 | assertFalse(p.filter(Utility.getHeapTuple(i + 1))); 57 | } 58 | } 59 | 60 | /** 61 | * JUnit suite target 62 | */ 63 | public static junit.framework.Test suite() { 64 | return new JUnit4TestAdapter(PredicateTest.class); 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /src/test/simpledb/RecordIdTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import junit.framework.JUnit4TestAdapter; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | import simpledb.storage.HeapPageId; 9 | import simpledb.storage.RecordId; 10 | import simpledb.systemtest.SimpleDbTestBase; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | public class RecordIdTest extends SimpleDbTestBase { 15 | 16 | private static RecordId hrid; 17 | private static RecordId hrid2; 18 | private static RecordId hrid3; 19 | private static RecordId hrid4; 20 | 21 | @Before public void createPids() { 22 | HeapPageId hpid = new HeapPageId(-1, 2); 23 | HeapPageId hpid2 = new HeapPageId(-1, 2); 24 | HeapPageId hpid3 = new HeapPageId(-2, 2); 25 | hrid = new RecordId(hpid, 3); 26 | hrid2 = new RecordId(hpid2, 3); 27 | hrid3 = new RecordId(hpid, 4); 28 | hrid4 = new RecordId(hpid3, 3); 29 | 30 | } 31 | 32 | /** 33 | * Unit test for RecordId.getPageId() 34 | */ 35 | @Test public void getPageId() { 36 | HeapPageId hpid = new HeapPageId(-1, 2); 37 | assertEquals(hpid, hrid.getPageId()); 38 | 39 | } 40 | 41 | /** 42 | * Unit test for RecordId.getTupleNumber() 43 | */ 44 | @Test public void tupleno() { 45 | assertEquals(3, hrid.getTupleNumber()); 46 | } 47 | 48 | /** 49 | * Unit test for RecordId.equals() 50 | */ 51 | @Test public void equals() { 52 | assertEquals(hrid, hrid2); 53 | assertEquals(hrid2, hrid); 54 | assertNotEquals(hrid, hrid3); 55 | assertNotEquals(hrid3, hrid); 56 | assertNotEquals(hrid2, hrid4); 57 | assertNotEquals(hrid4, hrid2); 58 | } 59 | 60 | /** 61 | * Unit test for RecordId.hashCode() 62 | */ 63 | @Test public void hCode() { 64 | assertEquals(hrid.hashCode(), hrid2.hashCode()); 65 | } 66 | 67 | /** 68 | * JUnit suite target 69 | */ 70 | public static junit.framework.Test suite() { 71 | return new JUnit4TestAdapter(RecordIdTest.class); 72 | } 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/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.common.Type; 9 | import simpledb.execution.Aggregator; 10 | import simpledb.execution.OpIterator; 11 | import simpledb.execution.StringAggregator; 12 | import simpledb.systemtest.SimpleDbTestBase; 13 | import static org.junit.Assert.assertEquals; 14 | import junit.framework.JUnit4TestAdapter; 15 | 16 | public class StringAggregatorTest extends SimpleDbTestBase { 17 | 18 | final int width1 = 2; 19 | OpIterator scan1; 20 | int[][] count = null; 21 | 22 | /** 23 | * Initialize each unit test 24 | */ 25 | @Before public void createTupleList() { 26 | this.scan1 = TestUtil.createTupleList(width1, 27 | new Object[] { 1, "a", 28 | 1, "b", 29 | 1, "c", 30 | 3, "d", 31 | 3, "e", 32 | 3, "f", 33 | 5, "g" }); 34 | 35 | // verify how the results progress after a few merges 36 | this.count = new int[][] { 37 | { 1, 1 }, 38 | { 1, 2 }, 39 | { 1, 3 }, 40 | { 1, 3, 3, 1 } 41 | }; 42 | 43 | } 44 | 45 | /** 46 | * Test String.mergeTupleIntoGroup() and iterator() over a COUNT 47 | */ 48 | @Test public void mergeCount() throws Exception { 49 | scan1.open(); 50 | StringAggregator agg = new StringAggregator(0, Type.INT_TYPE, 1, Aggregator.Op.COUNT); 51 | 52 | for (int[] step : count) { 53 | agg.mergeTupleIntoGroup(scan1.next()); 54 | OpIterator it = agg.iterator(); 55 | it.open(); 56 | TestUtil.matchAllTuples(TestUtil.createTupleList(width1, step), it); 57 | } 58 | } 59 | 60 | /** 61 | * Test StringAggregator.iterator() for OpIterator behaviour 62 | */ 63 | @Test public void testIterator() throws Exception { 64 | // first, populate the aggregator via sum over scan1 65 | scan1.open(); 66 | StringAggregator agg = new StringAggregator(0, Type.INT_TYPE, 1, Aggregator.Op.COUNT); 67 | try { 68 | while (true) 69 | agg.mergeTupleIntoGroup(scan1.next()); 70 | } catch (NoSuchElementException e) { 71 | // explicitly ignored 72 | } 73 | 74 | OpIterator it = agg.iterator(); 75 | it.open(); 76 | 77 | // verify it has three elements 78 | int count = 0; 79 | try { 80 | while (true) { 81 | it.next(); 82 | count++; 83 | } 84 | } catch (NoSuchElementException e) { 85 | // explicitly ignored 86 | } 87 | assertEquals(3, count); 88 | 89 | // rewind and try again 90 | it.rewind(); 91 | count = 0; 92 | try { 93 | while (true) { 94 | it.next(); 95 | count++; 96 | } 97 | } catch (NoSuchElementException e) { 98 | // explicitly ignored 99 | } 100 | assertEquals(3, count); 101 | 102 | // close it and check that we don't get anything 103 | it.close(); 104 | try { 105 | it.next(); 106 | throw new Exception("StringAggreator iterator yielded tuple after close"); 107 | } catch (Exception e) { 108 | // explicitly ignored 109 | } 110 | } 111 | 112 | /** 113 | * JUnit suite target 114 | */ 115 | public static junit.framework.Test suite() { 116 | return new JUnit4TestAdapter(StringAggregatorTest.class); 117 | } 118 | } 119 | 120 | -------------------------------------------------------------------------------- /src/test/simpledb/TransactionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | import static org.junit.Assert.assertEquals; 6 | import junit.framework.JUnit4TestAdapter; 7 | import simpledb.common.Database; 8 | import simpledb.common.Permissions; 9 | import simpledb.common.Utility; 10 | import simpledb.storage.*; 11 | import simpledb.transaction.TransactionId; 12 | 13 | import java.util.Iterator; 14 | 15 | public class TransactionTest extends TestUtil.CreateHeapFile { 16 | private PageId p0, p1, p2; 17 | private TransactionId tid1, tid2; 18 | 19 | // just so we have a pointer shorter than Database.getBufferPool() 20 | private BufferPool bp; 21 | 22 | /** 23 | * Set up initial resources for each unit test. 24 | */ 25 | @Before public void setUp() throws Exception { 26 | super.setUp(); 27 | 28 | // clear all state from the buffer pool 29 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 30 | 31 | // create a new empty HeapFile and populate it with three pages. 32 | // we should be able to add 504 tuples on an empty page. 33 | TransactionId tid = new TransactionId(); 34 | for (int i = 0; i < 1025; ++i) { 35 | empty.insertTuple(tid, Utility.getHeapTuple(i, 2)); 36 | } 37 | 38 | // if this fails, complain to the TA 39 | assertEquals(3, empty.numPages()); 40 | 41 | this.p0 = new HeapPageId(empty.getId(), 0); 42 | this.p1 = new HeapPageId(empty.getId(), 1); 43 | this.p2 = new HeapPageId(empty.getId(), 2); 44 | this.tid1 = new TransactionId(); 45 | this.tid2 = new TransactionId(); 46 | 47 | // forget about locks associated to tid, so they don't conflict with 48 | // test cases 49 | bp.getPage(tid, p0, Permissions.READ_WRITE).markDirty(true, tid); 50 | bp.getPage(tid, p1, Permissions.READ_WRITE).markDirty(true, tid); 51 | bp.getPage(tid, p2, Permissions.READ_WRITE).markDirty(true, tid); 52 | bp.flushAllPages(); 53 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 54 | } 55 | 56 | /** 57 | * Unit test for BufferPool.transactionComplete(). 58 | * Try to acquire locks that would conflict if old locks aren't released 59 | * during transactionComplete(). 60 | */ 61 | @Test public void attemptTransactionTwice() throws Exception { 62 | bp.getPage(tid1, p0, Permissions.READ_ONLY); 63 | bp.getPage(tid1, p1, Permissions.READ_WRITE); 64 | bp.transactionComplete(tid1, true); 65 | 66 | bp.getPage(tid2, p0, Permissions.READ_WRITE); 67 | bp.getPage(tid2, p0, Permissions.READ_WRITE); 68 | } 69 | 70 | /** 71 | * Common unit test code for BufferPool.transactionComplete() covering 72 | * commit and abort. Verify that commit persists changes to disk, and 73 | * that abort reverts pages to their previous on-disk state. 74 | */ 75 | public void testTransactionComplete(boolean commit) throws Exception { 76 | HeapPage p = (HeapPage) bp.getPage(tid1, p2, Permissions.READ_WRITE); 77 | 78 | Tuple t = Utility.getHeapTuple(new int[] { 6, 830 }); 79 | t.setRecordId(new RecordId(p2, 1)); 80 | 81 | p.insertTuple(t); 82 | p.markDirty(true, tid1); 83 | bp.transactionComplete(tid1, commit); 84 | 85 | // now, flush the buffer pool and access the page again from disk. 86 | bp = Database.resetBufferPool(BufferPool.DEFAULT_PAGES); 87 | p = (HeapPage) bp.getPage(tid2, p2, Permissions.READ_WRITE); 88 | Iterator it = p.iterator(); 89 | 90 | boolean found = false; 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 | if (f0.getValue() == 6 && f1.getValue() == 830) { 97 | found = true; 98 | break; 99 | } 100 | } 101 | 102 | assertEquals(commit, found); 103 | } 104 | 105 | /** 106 | * Unit test for BufferPool.transactionComplete() assuing commit. 107 | * Verify that a tuple inserted during a committed transaction is durable 108 | */ 109 | @Test public void commitTransaction() throws Exception { 110 | testTransactionComplete(true); 111 | } 112 | 113 | /** 114 | * Unit test for BufferPool.transactionComplete() assuming abort. 115 | * Verify that a tuple inserted during a committed transaction is durable 116 | */ 117 | @Test public void abortTransaction() throws Exception { 118 | testTransactionComplete(false); 119 | } 120 | 121 | /** 122 | * JUnit suite target 123 | */ 124 | public static junit.framework.Test suite() { 125 | return new JUnit4TestAdapter(TransactionTest.class); 126 | } 127 | 128 | } 129 | 130 | -------------------------------------------------------------------------------- /src/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.common.Utility; 9 | import simpledb.storage.*; 10 | import simpledb.systemtest.SimpleDbTestBase; 11 | 12 | public class TupleTest extends SimpleDbTestBase { 13 | 14 | /** 15 | * Unit test for Tuple.getField() and Tuple.setField() 16 | */ 17 | @Test public void modifyFields() { 18 | TupleDesc td = Utility.getTupleDesc(2); 19 | 20 | Tuple tup = new Tuple(td); 21 | tup.setField(0, new IntField(-1)); 22 | tup.setField(1, new IntField(0)); 23 | 24 | assertEquals(new IntField(-1), tup.getField(0)); 25 | assertEquals(new IntField(0), tup.getField(1)); 26 | 27 | tup.setField(0, new IntField(1)); 28 | tup.setField(1, new IntField(37)); 29 | 30 | assertEquals(new IntField(1), tup.getField(0)); 31 | assertEquals(new IntField(37), tup.getField(1)); 32 | } 33 | 34 | /** 35 | * Unit test for Tuple.getTupleDesc() 36 | */ 37 | @Test public void getTupleDesc() { 38 | TupleDesc td = Utility.getTupleDesc(5); 39 | Tuple tup = new Tuple(td); 40 | assertEquals(td, tup.getTupleDesc()); 41 | } 42 | 43 | /** 44 | * Unit test for Tuple.getRecordId() and Tuple.setRecordId() 45 | */ 46 | @Test public void modifyRecordId() { 47 | Tuple tup1 = new Tuple(Utility.getTupleDesc(1)); 48 | HeapPageId pid1 = new HeapPageId(0,0); 49 | RecordId rid1 = new RecordId(pid1, 0); 50 | tup1.setRecordId(rid1); 51 | 52 | try { 53 | assertEquals(rid1, tup1.getRecordId()); 54 | } catch (java.lang.UnsupportedOperationException e) { 55 | //rethrow the exception with an explanation 56 | throw new UnsupportedOperationException("modifyRecordId() test failed due to " + 57 | "RecordId.equals() not being implemented. This is not required for Lab 1, " + 58 | "but should pass when you do implement the RecordId class."); 59 | } 60 | } 61 | 62 | /** 63 | * JUnit suite target 64 | */ 65 | public static junit.framework.Test suite() { 66 | return new JUnit4TestAdapter(TupleTest.class); 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/test/simpledb/systemtest/AbortEvictionTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.io.IOException; 6 | import java.util.Collections; 7 | 8 | import org.junit.Test; 9 | 10 | import simpledb.common.Database; 11 | import simpledb.common.DbException; 12 | import simpledb.common.Utility; 13 | import simpledb.execution.Insert; 14 | import simpledb.execution.SeqScan; 15 | import simpledb.storage.*; 16 | import simpledb.transaction.Transaction; 17 | import simpledb.transaction.TransactionAbortedException; 18 | 19 | public class AbortEvictionTest extends SimpleDbTestBase { 20 | // Note: This is a direct copy of the EvictTest method, 21 | // but the autograder won't necessarily have EvictTest when it runs 22 | // AbortEvictionTest, so we can't just cal the EvictTest method 23 | public static void insertRow(HeapFile f, Transaction t) throws DbException, 24 | TransactionAbortedException { 25 | // Create a row to insert 26 | TupleDesc twoIntColumns = Utility.getTupleDesc(2); 27 | Tuple value = new Tuple(twoIntColumns); 28 | value.setField(0, new IntField(-42)); 29 | value.setField(1, new IntField(-43)); 30 | TupleIterator insertRow = new TupleIterator(Utility.getTupleDesc(2), Collections.singletonList(value)); 31 | 32 | // Insert the row 33 | Insert insert = new Insert(t.getId(), insertRow, f.getId()); 34 | insert.open(); 35 | Tuple result = insert.next(); 36 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, result.getTupleDesc()); 37 | assertEquals(1, ((IntField)result.getField(0)).getValue()); 38 | assertFalse(insert.hasNext()); 39 | insert.close(); 40 | } 41 | 42 | // Note: This is a direct copy of the EvictTest method, 43 | // but the autograder won't necessarily have EvictTest when it runs 44 | // AbortEvictionTest, so we can't just cal the EvictTest method 45 | public static boolean findMagicTuple(HeapFile f, Transaction t) 46 | throws DbException, TransactionAbortedException { 47 | SeqScan ss = new SeqScan(t.getId(), f.getId(), ""); 48 | boolean found = false; 49 | ss.open(); 50 | while (ss.hasNext()) { 51 | Tuple v = ss.next(); 52 | int v0 = ((IntField)v.getField(0)).getValue(); 53 | int v1 = ((IntField)v.getField(1)).getValue(); 54 | if (v0 == -42 && v1 == -43) { 55 | assertFalse(found); 56 | found = true; 57 | } 58 | } 59 | ss.close(); 60 | return found; 61 | } 62 | 63 | /** Aborts a transaction and ensures that its effects were actually undone. 64 | * This requires dirty pages to not get flushed to disk. 65 | */ 66 | @Test public void testDoNotEvictDirtyPages() 67 | throws IOException, DbException, TransactionAbortedException { 68 | // Allocate a file with ~10 pages of data 69 | HeapFile f = SystemTestUtil.createRandomHeapFile(2, 512*10, null, null); 70 | Database.resetBufferPool(2); 71 | 72 | // BEGIN TRANSACTION 73 | Transaction t = new Transaction(); 74 | t.start(); 75 | 76 | // Insert a new row 77 | AbortEvictionTest.insertRow(f, t); 78 | 79 | // The tuple must exist in the table 80 | boolean found = AbortEvictionTest.findMagicTuple(f, t); 81 | assertTrue(found); 82 | // ABORT 83 | t.transactionComplete(true); 84 | 85 | // A second transaction must not find the tuple 86 | t = new Transaction(); 87 | t.start(); 88 | found = AbortEvictionTest.findMagicTuple(f, t); 89 | assertFalse(found); 90 | t.commit(); 91 | } 92 | 93 | /** Make test compatible with older version of ant. */ 94 | public static junit.framework.Test suite() { 95 | return new junit.framework.JUnit4TestAdapter(AbortEvictionTest.class); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/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 java.util.List; 8 | 9 | import simpledb.common.DbException; 10 | import simpledb.execution.Delete; 11 | import simpledb.execution.Filter; 12 | import simpledb.execution.Predicate; 13 | import simpledb.execution.SeqScan; 14 | import simpledb.storage.HeapFile; 15 | import simpledb.storage.IntField; 16 | import simpledb.storage.Tuple; 17 | import simpledb.transaction.TransactionAbortedException; 18 | import simpledb.transaction.TransactionId; 19 | 20 | public class DeleteTest extends FilterBase { 21 | List> expectedTuples = null; 22 | 23 | @Override 24 | protected int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 25 | throws DbException, TransactionAbortedException { 26 | SeqScan ss = new SeqScan(tid, table.getId(), ""); 27 | Filter filter = new Filter(predicate, ss); 28 | Delete deleteOperator = new Delete(tid, filter); 29 | // Query q = new Query(deleteOperator, tid); 30 | 31 | // q.start(); 32 | deleteOperator.open(); 33 | boolean hasResult = false; 34 | int result = -1; 35 | while (deleteOperator.hasNext()) { 36 | Tuple t = deleteOperator.next(); 37 | assertFalse(hasResult); 38 | hasResult = true; 39 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, t.getTupleDesc()); 40 | result = ((IntField) t.getField(0)).getValue(); 41 | } 42 | assertTrue(hasResult); 43 | 44 | deleteOperator.close(); 45 | 46 | // As part of the same transaction, scan the table 47 | if (result == 0) { 48 | // Deleted zero tuples: all tuples still in table 49 | expectedTuples = createdTuples; 50 | } else { 51 | assert result == createdTuples.size(); 52 | expectedTuples = new ArrayList<>(); 53 | } 54 | SystemTestUtil.matchTuples(table, tid, expectedTuples); 55 | return result; 56 | } 57 | 58 | @Override 59 | protected void validateAfter(HeapFile table) 60 | throws DbException, TransactionAbortedException, IOException { 61 | // As part of a different transaction, scan the table 62 | SystemTestUtil.matchTuples(table, expectedTuples); 63 | } 64 | 65 | /** Make test compatible with older version of ant. */ 66 | public static junit.framework.Test suite() { 67 | return new junit.framework.JUnit4TestAdapter(DeleteTest.class); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/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.Collections; 7 | 8 | import org.junit.Test; 9 | 10 | import org.junit.Assert; 11 | import simpledb.common.Database; 12 | import simpledb.common.DbException; 13 | import simpledb.common.Utility; 14 | import simpledb.execution.Insert; 15 | import simpledb.execution.SeqScan; 16 | import simpledb.storage.*; 17 | import simpledb.transaction.Transaction; 18 | import simpledb.transaction.TransactionAbortedException; 19 | import simpledb.transaction.TransactionId; 20 | 21 | /** 22 | * Creates a heap file with 1024*500 tuples with two integer fields each. Clears the buffer pool, 23 | * and performs a sequential scan through all of the pages. If the growth in JVM usage 24 | * is greater than 2 MB due to the scan, the test fails. Otherwise, the page eviction policy seems 25 | * to have worked. 26 | */ 27 | public class EvictionTest extends SimpleDbTestBase { 28 | private static final long MEMORY_LIMIT_IN_MB = 5; 29 | private static final int BUFFER_PAGES = 16; 30 | 31 | @Test public void testHeapFileScanWithManyPages() throws IOException, DbException, TransactionAbortedException { 32 | System.out.println("EvictionTest creating large table"); 33 | HeapFile f = SystemTestUtil.createRandomHeapFile(2, 1024*500, null, null); 34 | System.out.println("EvictionTest scanning large table"); 35 | Database.resetBufferPool(BUFFER_PAGES); 36 | long beginMem = SystemTestUtil.getMemoryFootprint(); 37 | TransactionId tid = new TransactionId(); 38 | SeqScan scan = new SeqScan(tid, f.getId(), ""); 39 | scan.open(); 40 | while (scan.hasNext()) { 41 | scan.next(); 42 | } 43 | System.out.println("EvictionTest scan complete, testing memory usage of scan"); 44 | long endMem = SystemTestUtil.getMemoryFootprint(); 45 | long memDiff = (endMem - beginMem) / (1<<20); 46 | if (memDiff > MEMORY_LIMIT_IN_MB) { 47 | Assert.fail("Did not evict enough pages. Scan took " + memDiff + " MB of RAM, when limit was " + MEMORY_LIMIT_IN_MB); 48 | } 49 | } 50 | 51 | public static void insertRow(HeapFile f, Transaction t) throws DbException, 52 | TransactionAbortedException { 53 | // Create a row to insert 54 | TupleDesc twoIntColumns = Utility.getTupleDesc(2); 55 | Tuple value = new Tuple(twoIntColumns); 56 | value.setField(0, new IntField(-42)); 57 | value.setField(1, new IntField(-43)); 58 | TupleIterator insertRow = new TupleIterator(Utility.getTupleDesc(2), Collections.singletonList(value)); 59 | 60 | // Insert the row 61 | Insert insert = new Insert(t.getId(), insertRow, f.getId()); 62 | insert.open(); 63 | Tuple result = insert.next(); 64 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, result.getTupleDesc()); 65 | assertEquals(1, ((IntField)result.getField(0)).getValue()); 66 | assertFalse(insert.hasNext()); 67 | insert.close(); 68 | } 69 | 70 | public static boolean findMagicTuple(HeapFile f, Transaction t) 71 | throws DbException, TransactionAbortedException { 72 | SeqScan ss = new SeqScan(t.getId(), f.getId(), ""); 73 | boolean found = false; 74 | ss.open(); 75 | while (ss.hasNext()) { 76 | Tuple v = ss.next(); 77 | int v0 = ((IntField)v.getField(0)).getValue(); 78 | int v1 = ((IntField)v.getField(1)).getValue(); 79 | if (v0 == -42 && v1 == -43) { 80 | assertFalse(found); 81 | found = true; 82 | } 83 | } 84 | ss.close(); 85 | return found; 86 | } 87 | 88 | /** Make test compatible with older version of ant. */ 89 | public static junit.framework.Test suite() { 90 | return new junit.framework.JUnit4TestAdapter(EvictionTest.class); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/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.List; 7 | import java.util.Map; 8 | 9 | import static org.junit.Assert.*; 10 | import org.junit.Test; 11 | 12 | import simpledb.common.Database; 13 | import simpledb.common.DbException; 14 | import simpledb.common.Utility; 15 | import simpledb.execution.Predicate; 16 | import simpledb.storage.HeapFile; 17 | import simpledb.storage.IntField; 18 | import simpledb.transaction.TransactionAbortedException; 19 | import simpledb.transaction.TransactionId; 20 | 21 | public abstract class FilterBase extends SimpleDbTestBase { 22 | private static final int COLUMNS = 3; 23 | private static final int ROWS = 1097; 24 | 25 | /** Should apply the predicate to table. This will be executed in transaction tid. */ 26 | protected abstract int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 27 | throws DbException, TransactionAbortedException; 28 | 29 | /** Optional hook for validating database state after applyPredicate. */ 30 | protected void validateAfter(HeapFile table) 31 | throws DbException, TransactionAbortedException, IOException {} 32 | 33 | protected List> createdTuples; 34 | 35 | private int runTransactionForPredicate(HeapFile table, Predicate predicate) 36 | throws IOException, DbException, TransactionAbortedException { 37 | TransactionId tid = new TransactionId(); 38 | int result = applyPredicate(table, tid, predicate); 39 | Database.getBufferPool().transactionComplete(tid); 40 | return result; 41 | } 42 | 43 | private void validatePredicate(int column, int columnValue, int trueValue, int falseValue, 44 | Predicate.Op operation) throws IOException, DbException, TransactionAbortedException { 45 | // Test the true value 46 | HeapFile f = createTable(column, columnValue); 47 | Predicate predicate = new Predicate(column, operation, new IntField(trueValue)); 48 | assertEquals(ROWS, runTransactionForPredicate(f, predicate)); 49 | f = Utility.openHeapFile(COLUMNS, f.getFile()); 50 | validateAfter(f); 51 | 52 | // Test the false value 53 | f = createTable(column, columnValue); 54 | predicate = new Predicate(column, operation, new IntField(falseValue)); 55 | assertEquals(0, runTransactionForPredicate(f, predicate)); 56 | f = Utility.openHeapFile(COLUMNS, f.getFile()); 57 | validateAfter(f); 58 | } 59 | 60 | private HeapFile createTable(int column, int columnValue) 61 | throws IOException { 62 | Map columnSpecification = new HashMap<>(); 63 | columnSpecification.put(column, columnValue); 64 | createdTuples = new ArrayList<>(); 65 | return SystemTestUtil.createRandomHeapFile( 66 | COLUMNS, ROWS, columnSpecification, createdTuples); 67 | } 68 | 69 | @Test public void testEquals() throws 70 | DbException, TransactionAbortedException, IOException { 71 | validatePredicate(0, 1, 1, 2, Predicate.Op.EQUALS); 72 | } 73 | 74 | @Test public void testLessThan() throws 75 | DbException, TransactionAbortedException, IOException { 76 | validatePredicate(1, 1, 2, 1, Predicate.Op.LESS_THAN); 77 | } 78 | 79 | @Test public void testLessThanOrEq() throws 80 | DbException, TransactionAbortedException, IOException { 81 | validatePredicate(2, 42, 42, 41, Predicate.Op.LESS_THAN_OR_EQ); 82 | } 83 | 84 | @Test public void testGreaterThan() throws 85 | DbException, TransactionAbortedException, IOException { 86 | validatePredicate(2, 42, 41, 42, Predicate.Op.GREATER_THAN); 87 | } 88 | 89 | @Test public void testGreaterThanOrEq() throws 90 | DbException, TransactionAbortedException, IOException { 91 | validatePredicate(2, 42, 42, 43, Predicate.Op.GREATER_THAN_OR_EQ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/simpledb/systemtest/FilterTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import simpledb.common.DbException; 6 | import simpledb.execution.Filter; 7 | import simpledb.execution.Predicate; 8 | import simpledb.execution.SeqScan; 9 | import simpledb.storage.HeapFile; 10 | import simpledb.transaction.TransactionAbortedException; 11 | import simpledb.transaction.TransactionId; 12 | 13 | public class FilterTest extends FilterBase { 14 | @Override 15 | protected int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) 16 | throws DbException, TransactionAbortedException { 17 | SeqScan ss = new SeqScan(tid, table.getId(), ""); 18 | Filter filter = new Filter(predicate, ss); 19 | filter.open(); 20 | 21 | int resultCount = 0; 22 | while (filter.hasNext()) { 23 | assertNotNull(filter.next()); 24 | resultCount += 1; 25 | } 26 | 27 | filter.close(); 28 | return resultCount; 29 | } 30 | 31 | /** Make test compatible with older version of ant. */ 32 | public static junit.framework.Test suite() { 33 | return new junit.framework.JUnit4TestAdapter(FilterTest.class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/simpledb/systemtest/InsertTest.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import static org.junit.Assert.*; 8 | import org.junit.Test; 9 | import simpledb.common.Database; 10 | import simpledb.common.DbException; 11 | import simpledb.execution.Insert; 12 | import simpledb.execution.SeqScan; 13 | import simpledb.storage.HeapFile; 14 | import simpledb.storage.IntField; 15 | import simpledb.storage.Tuple; 16 | import simpledb.transaction.TransactionAbortedException; 17 | import simpledb.transaction.TransactionId; 18 | 19 | public class InsertTest extends SimpleDbTestBase { 20 | private void validateInsert(int columns, int sourceRows, int destinationRows) 21 | throws DbException, IOException, TransactionAbortedException { 22 | // Create the two tables 23 | List> sourceTuples = new ArrayList<>(); 24 | HeapFile source = SystemTestUtil.createRandomHeapFile( 25 | columns, sourceRows, null, sourceTuples); 26 | assert sourceTuples.size() == sourceRows; 27 | List> destinationTuples = new ArrayList<>(); 28 | HeapFile destination = SystemTestUtil.createRandomHeapFile( 29 | columns, destinationRows, null, destinationTuples); 30 | assert destinationTuples.size() == destinationRows; 31 | 32 | // Insert source into destination 33 | TransactionId tid = new TransactionId(); 34 | SeqScan ss = new SeqScan(tid, source.getId(), ""); 35 | Insert insOp = new Insert(tid, ss, destination.getId()); 36 | 37 | // Query q = new Query(insOp, tid); 38 | insOp.open(); 39 | boolean hasResult = false; 40 | while (insOp.hasNext()) { 41 | Tuple tup = insOp.next(); 42 | assertFalse(hasResult); 43 | hasResult = true; 44 | assertEquals(SystemTestUtil.SINGLE_INT_DESCRIPTOR, tup.getTupleDesc()); 45 | assertEquals(sourceRows, ((IntField) tup.getField(0)).getValue()); 46 | } 47 | assertTrue(hasResult); 48 | insOp.close(); 49 | 50 | // As part of the same transaction, scan the table 51 | sourceTuples.addAll(destinationTuples); 52 | SystemTestUtil.matchTuples(destination, tid, sourceTuples); 53 | 54 | // As part of a different transaction, scan the table 55 | Database.getBufferPool().transactionComplete(tid); 56 | Database.getBufferPool().flushAllPages(); 57 | SystemTestUtil.matchTuples(destination, sourceTuples); 58 | } 59 | 60 | @Test public void testEmptyToEmpty() 61 | throws IOException, DbException, TransactionAbortedException { 62 | validateInsert(3, 0, 0); 63 | } 64 | 65 | @Test public void testEmptyToOne() 66 | throws IOException, DbException, TransactionAbortedException { 67 | validateInsert(8, 0, 1); 68 | } 69 | 70 | @Test public void testOneToEmpty() 71 | throws IOException, DbException, TransactionAbortedException { 72 | validateInsert(3, 1, 0); 73 | } 74 | 75 | @Test public void testOneToOne() 76 | throws IOException, DbException, TransactionAbortedException { 77 | validateInsert(1, 1, 1); 78 | } 79 | 80 | /** Make test compatible with older version of ant. */ 81 | public static junit.framework.Test suite() { 82 | return new junit.framework.JUnit4TestAdapter(InsertTest.class); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/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 | import java.util.List; 7 | import java.util.Map; 8 | 9 | import org.junit.Test; 10 | 11 | import simpledb.common.Database; 12 | import simpledb.common.DbException; 13 | import simpledb.execution.Join; 14 | import simpledb.execution.JoinPredicate; 15 | import simpledb.execution.Predicate; 16 | import simpledb.execution.SeqScan; 17 | import simpledb.storage.HeapFile; 18 | import simpledb.transaction.TransactionAbortedException; 19 | import simpledb.transaction.TransactionId; 20 | 21 | public class JoinTest extends SimpleDbTestBase { 22 | private static final int COLUMNS = 2; 23 | public void validateJoin(int table1ColumnValue, int table1Rows, int table2ColumnValue, 24 | int table2Rows) 25 | throws IOException, DbException, TransactionAbortedException { 26 | // Create the two tables 27 | Map columnSpecification = new HashMap<>(); 28 | columnSpecification.put(0, table1ColumnValue); 29 | List> t1Tuples = new ArrayList<>(); 30 | HeapFile table1 = SystemTestUtil.createRandomHeapFile( 31 | COLUMNS, table1Rows, columnSpecification, t1Tuples); 32 | assert t1Tuples.size() == table1Rows; 33 | 34 | columnSpecification.put(0, table2ColumnValue); 35 | List> t2Tuples = new ArrayList<>(); 36 | HeapFile table2 = SystemTestUtil.createRandomHeapFile( 37 | COLUMNS, table2Rows, columnSpecification, t2Tuples); 38 | assert t2Tuples.size() == table2Rows; 39 | 40 | // Generate the expected results 41 | List> expectedResults = new ArrayList<>(); 42 | for (List t1 : t1Tuples) { 43 | for (List t2 : t2Tuples) { 44 | // If the columns match, join the tuples 45 | if (t1.get(0).equals(t2.get(0))) { 46 | List out = new ArrayList<>(t1); 47 | out.addAll(t2); 48 | expectedResults.add(out); 49 | } 50 | } 51 | } 52 | 53 | // Begin the join 54 | TransactionId tid = new TransactionId(); 55 | SeqScan ss1 = new SeqScan(tid, table1.getId(), ""); 56 | SeqScan ss2 = new SeqScan(tid, table2.getId(), ""); 57 | JoinPredicate p = new JoinPredicate(0, Predicate.Op.EQUALS, 0); 58 | Join joinOp = new Join(p, ss1, ss2); 59 | 60 | // test the join results 61 | SystemTestUtil.matchTuples(joinOp, expectedResults); 62 | 63 | joinOp.close(); 64 | Database.getBufferPool().transactionComplete(tid); 65 | } 66 | 67 | @Test public void testSingleMatch() 68 | throws IOException, DbException, TransactionAbortedException { 69 | validateJoin(1, 1, 1, 1); 70 | } 71 | 72 | @Test public void testNoMatch() 73 | throws IOException, DbException, TransactionAbortedException { 74 | validateJoin(1, 2, 2, 10); 75 | } 76 | 77 | @Test public void testMultipleMatch() 78 | throws IOException, DbException, TransactionAbortedException { 79 | validateJoin(1, 3, 1, 3); 80 | } 81 | 82 | /** Make test compatible with older version of ant. */ 83 | public static junit.framework.Test suite() { 84 | return new junit.framework.JUnit4TestAdapter(JoinTest.class); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/simpledb/systemtest/SimpleDbTestBase.java: -------------------------------------------------------------------------------- 1 | package simpledb.systemtest; 2 | 3 | import org.junit.Before; 4 | 5 | import simpledb.common.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 | -------------------------------------------------------------------------------- /tools/check_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # make sure git has no un commit files 4 | if [ -n "$(git status --untracked-files=no --porcelain)" ]; then 5 | echo "Please commit your change before run this shell, un commit files:" 6 | git status --untracked-files=no --porcelain 7 | exit -1 8 | fi -------------------------------------------------------------------------------- /tools/codestyle/HEADER: -------------------------------------------------------------------------------- 1 | Licensed to the Apache Software Foundation (ASF) under one or more 2 | contributor license agreements. See the NOTICE file distributed with 3 | this work for additional information regarding copyright ownership. 4 | The ASF licenses this file to You under the Apache License, Version 2.0 5 | (the "License"); you may not use this file except in compliance with 6 | the License. You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. --------------------------------------------------------------------------------