文章目录
参考文档
目录
- 过滤、连接、orderby
- 聚集函数
- 元组插入删除
- 页面替换eviction
实验一:Join和filter
predicate:
三个域:
- filed:域的id
- op:操作符
- operand:操作数
函数实现:
- filter()
- toString()
public boolean filter(Tuple t) {
// some code goes here
return t.getField(this.field).compare(op, this.operand); // 这个compare已经由某一个域给定义好了int和string的比较规则
}
joinpredicte
- 两个元组内字段比较,用于join运算
域:
- 两个元组的域
- 操作符
函数
- filter()
public boolean filter(Tuple t1, Tuple t2) {
// some code goes here
return t1.getField(this.field_a).compare(op, t2.getField(this.field_b));
}
filter
- 进行过滤操作
- super.close()和open()
- fetchNext()
protected Tuple fetchNext() throws NoSuchElementException,
TransactionAbortedException, DbException {
// some code goes here
while(child.hasNext()) {
Tuple tuple = child.next();
if (this.p.filter(tuple)) // 通过过滤器看看是否满足条件
return tuple;
}
return null;
}
join
域:
- private final JoinPredicate joinPredicate; 双元组比较
- private OpIterator child1; 元组迭代器1
- private OpIterator child2; 元组迭代器2
- private Tuple t; 临时元组,保存上次迭代用的 child1 的 Tuple
这个主要用于下次遍历的时候,继续使用当前元组,因为可能有多个符合条件的结果,也就是一对多,所以t1不能够直接跳到下一条,使用临时值保存当前元组。
函数:
- fetchNext()
protected Tuple fetchNext() throws TransactionAbortedException, DbException {
// some code goes here
// t 的意义就是 笛卡尔积, 一个可能对多个相等
while (child1.hasNext() || t != null){
if(child1.hasNext() && t == null){
t = child1.next();
}
while(child2.hasNext()){
Tuple t2 = child2.next();
if(joinPredicate.filter(t, t2)){
TupleDesc td1 = t.getTupleDesc();
TupleDesc td2 = t2.getTupleDesc();
// 合并
TupleDesc tupleDesc = TupleDesc.merge(td1, td2);
Tuple newTuple = new Tuple(tupleDesc);
// 设置路径, 暂时不先设置
// newTuple.setRecordId(t.getRecordId());
int i = 0;
for (; i < td1.numFields(); i++) {
newTuple.setField(i, t.getField(i));
}
for (int j = 0; j < td2.numFields(); j++) {
newTuple.setField(i + j, t2.getField(j));
}
// 遍历完,t2重头,t走向下一个
if(!child2.hasNext()){
child2.rewind();
t = null;
}
return newTuple;
}
}
// 重置 child2
child2.rewind();
t = null;
}
return null;
}
实验二:Aggregates
Aggregator
域
-
child :表迭代器
-
afield:需要聚合的域id
-
gfield:需要分组的域id
-
gfieldType:需要分组的域类型,因为没有表信息,所以只能通过父类传递。
-
AggHandler:自定义的handler,用来实现不同的聚合函数。由op来决定创建什么类型的聚合函数。
-
- 保存答案的map{key:分组,value:每组聚合函数的结果}
- handle方法,抽象方法,实现具体的聚集函数逻辑。
方法
-
mergeTotuple:通过自定义的handler,更新handler中的结果集合。
-
iterator:迭代器,将结果集合转化成tuple的形式。然后返回tuple列表的迭代器。
-
- tuple表头是{groupVal,aggVal}
- tuple内容是map中的键值对
代码
public OpIterator iterator() {
// some code goes here
// throw new
// UnsupportedOperationException("please implement me for lab2");
Map<Field, IntField> ans = handler.ans;
List<Tuple>tuples = new ArrayList<>();
TupleDesc tupleDesc; // 创建迭代器的时候,需要用到
if (gfield == NO_GROUPING) { // 如果没有group
// 创建tuple
tupleDesc = new TupleDesc(new Type[]{Type.INT_TYPE}, new String[]{"aggregateVal"});
Tuple tuple = new Tuple(tupleDesc);
// 把查询到的结果放入tuple中
tuple.setField(0, ans.get(null));
// 不要忘了把创建的tuple放入到结果集中
tuples.add(tuple);
}
else { // 如果含有group
tupleDesc = new TupleDesc(new Type[]{this.gfieldtype, Type.INT_TYPE}, new String[]{"groupVal", "aggregateVal"});
for (Map.Entry<Field, IntField> entry : ans.entrySet()) {
Tuple tuple = new Tuple(tupleDesc);
tuple.setField(0, entry.getKey());
tuple.setField(1, entry.getValue());
tuples.add(tuple);
}
}
return new TupleIterator(tupleDesc, tuples);
}
public void mergeTupleIntoGroup(Tuple tup) {
// some code goes here
// 获取对应域的值
IntField afield = (IntField) tup.getField(this.afield);
Field gfield = this.gfield == NO_GROUPING ? null : tup.getField(this.gfield);
handler.handle(gfield, afield);
}
private abstract class AggHandler {
private Map<Field, IntField>ans;
abstract void handle(Field gfield, IntField afield); // group by不知道什么类型,但是聚集函数得到的结果一定是int
public AggHandler() {
ans = new HashMap<>();
}
public Map<Field, IntField> getAns() {
return this.ans;
}
}
private class MinHandler extends AggHandler {
@Override
void handle(Field gfield, IntField afield) {
int x = afield.getValue();
Map<Field, IntField>ans = this.getAns();
if (ans.containsKey(gfield)) {
ans.put(gfield, new IntField(Math.min(x, ans.get(gfield).getValue())));
}
else ans.put(gfield, new IntField(x));
}
}
public IntegerAggregator(int gbfield, Type gbfieldtype, int afield, Op what) {
// some code goes here
this.gfield = gbfield;
this.afield = afield;
this.gfieldtype = gbfieldtype;
this.op = what;
switch (what) {
case MIN:
this.handler = new MinHandler(); break;
case AVG:
this.handler = new AvgHandler(); break;
case SUM:
this.handler = new SumHandler(); break;
case COUNT:
this.handler = new CountHandler(); break;
case MAX:
this.handler = new MaxHandler(); break;
default:
throw new IllegalArgumentException("聚合器不支持当前运算符");
}
}
Aggregates
域:
- child:表迭代器入口
- afield:聚合的域id
- gfield:分组的域Id
- aop: 聚合函数的id
- aggregator:集合器,上面的一种,由afield决定使用什么类型的
- iterator:结果集合的迭代器
方法:
- 构造函数
- getTupleDesc():返回形如max(score)这种的表头。分是否有gfeild的来决定列数
代码:
public TupleDesc getTupleDesc() {
// some code goes here
// String afiledName = this.aop.name() + "(" + this.aggregateFieldName() + ")"; // max(id);
// Type aType = this.child.getTupleDesc().getFieldType(this.afield);
// if (this.groupFieldName() != null) {
// String gfiledName = this.groupFieldName();
// Type gType = this.child.getTupleDesc().getFieldType(this.gfield);
// return new TupleDesc(new Type[]{gType, aType},new String[]{gfiledName, afiledName});
// }
// else {
// return new TupleDesc(new Type[]{aType}, new String[]{afiledName});
// }
List<Type>types = new ArrayList<>();
List<String>names = new ArrayList<>();
if (groupFieldName() != null) {
types.add(this.child.getTupleDesc().getFieldType(this.gfield));
names.add(groupFieldName());
}
types.add(this.child.getTupleDesc().getFieldType(this.afield));
names.add(this.aop.name() + "(" + aggregateFieldName() + ")"); // 类似于max(id), AVG(score)这种
if (this.aop.equals(Aggregator.Op.SUM_COUNT)) { // 如果有
types.add(Type.INT_TYPE);
names.add("COUNT");
}
return new TupleDesc(types.toArray(new Type[types.size()]), names.toArray(new String[names.size()]));
}
// 这里可以使用工厂模式进行改写
public Aggregate(OpIterator child, int afield, int gfield, Aggregator.Op aop) {
// some code goes here
this.child = child;
this.afield = afield;
this.gfield = gfield;
this.aop = aop;
Type gfieldtype;
if (gfield == NO_GROUPING) gfieldtype = null;
else gfieldtype = this.child.getTupleDesc().getFieldType(this.gfield);
if (this.child.getTupleDesc().getFieldType(afield).equals(Type.STRING_TYPE))
this.aggregator = new StringAggregator(gfield, this.child.getTupleDesc().getFieldType(this.gfield), afield, aop);
else this.aggregator = new IntegerAggregator(gfield, gfieldtype, afield, aop);
// desc = this.getTupleDesc();
}
实验三:HeapPage和HeapFile,bufferpool完善
heapPage:
增加事务id
插入和删除
heapFile
插入和删除
buffer pool
-
flush
-
insertTuple:这个逻辑在BufferPoolWriteTest中有,但是没有从bufferpool中获取,修改test中的insert内容
-
- 首先,如果有可以插入的就插入
- 其次,没有可以插入的,创建新页面,插入文件
- 最后,从缓存中获取页面。
-
-
- 缓存中页面的逻辑是,如果没有从文件中读取,否则直接返回
- 另外缓存中的get需要重构,双向链表,缓存满了,末尾淘汰。
-
-
DeleteTuple
-
iterator中的逻辑出错了
实验四:插入和删除
域
- 事务id
- 插入的迭代器
- 插入的表
- 结果tupleDesc
- 插入标记
方法
直接调用Bufferpool.insert()和delete方法就行了。
实验五:淘汰策略
双向链表实现LRU。
- 每次使用,将页面放头。
实验六:连接以及过滤查询
- 创建文件
- 放入内容,注意换行
- 执行测试文件
实验7:查询解析器使用
- 创建文件
- 执行命令转换,前提是编译ant通过
- 最后执行命令,进入命令行
- 输入查询,得到结果。
问题
-
recordId在Join连接之后,为什么设置成t1的呢?
-
- 应该设置成临时表的
- 但是没有临时表
-
为什么继承于Operator而不是Implemnent OpIterator
-
- 因为next和hasNext()是重复的方法
-
为什么open和close需要super.open和super.close呢?
-
- 因为这两个方法是重写于父类。判断的逻辑在父类的hasNext()和next()
- 所以要先改父类的状态,从而通过上面的两个办法,之后在修改自己的方法。
-
创建新的tuple的时候recordId应该是多少?
-
setchildren的意义在哪里?
-
sum_count如何实现的?另外为什么sum_count需要在desc中新增一列?
-
- 因为两个操作,所以新增了一列。
-
插入和删除元组的时候,是否应该立即写文件呢?如果不是,什么时候进行写文件呢?
-
为什么在bufferpool中打脏写的标记?不是在DbFile的时候打?
-
末尾淘汰的时候,是否需要刷盘呢?flush和删除是两个操作。这两个操作由更高层的进行控制。如果需要立即刷盘,就delete(),flush,否则就不需要。
-
iterator中的逻辑,应该是本页没有,找第一个有的,如果都没有才结束。
11. 编码问题
修改build中的copile如下
target name="compile" description="Compile code">
<javac srcdir="${src}/java" destdir="${build.src}" debug="on" deprecation="on" optimize="off" includes="**">
<!--给编译器指定编码,防止出现:"警告: 编码 GBK 的不可映射字符"-->
<compilerarg line="-encoding UTF-8 "/>
<classpath refid="classpath.base" />
</javac>
<Compile srcdir="${src}/java" destdir="${build.src}">
<classpath refid="classpath.base"/>
</Compile>
<copy todir="${build}" flatten="true">
<fileset dir="${src}">
<include name="bin/*.sh"/>
</fileset>
</copy>
</target>
总结
- debug的总结:由于碰见了no_group_avg的问题。进行代码调试,对自己写的代码打端点,tuple忘了加入到tuples中。
- 插入和删除的时候,一定及时更新缓存。而不用立刻写文件。所以有了脏页。