static RC RollBackUpdate(SM_DbHandle &db, Event &event, RID &rid)
{
    RC rc;

    printf("QL: Undoing update.\n");
    RM_FileHandle *pTableHandle;
    if ((rc = db.FindTableHandle(event.tableName.c_str(), pTableHandle)))
        return rc;

    RM_Record record;
    if ((rc = pTableHandle->GetRec(rid, record)))
        return rc;

    char *data;
    if ((rc = record.GetData(data)))
        return rc;

    SM_Attribute *pAttr;
    if ((rc = db.FindAttribute(event.tableName.c_str(), event.attrName.c_str(), pAttr)))
        return rc;

    // If indexed, remove the old entry.
    IX_IndexHandle *pIndexHandle;
    if (pAttr->hasIndex) {
        if ((rc = db.FindIndexHandle(event.tableName.c_str(), event.attrName.c_str(), pIndexHandle))) 
            return rc;
        if ((rc = pIndexHandle->DeleteEntry(data + pAttr->attrOffset, rid))) 
            return rc;
    }

    // Restore the record.
    int recordSize = pTableHandle->header.recordSize;
    memcpy(data, event.record, recordSize);

    // Update the record.
    if ((rc = pTableHandle->UpdateRec(record))) 
        return rc;

    // If indexed, insert the new entry.
    if (pAttr->hasIndex) {
        if ((rc = pIndexHandle->InsertEntry(data + pAttr->attrOffset, rid))) 
            return rc;
    }

    return 0;
}
RC QL_Manager::Update()
{
    RC rc;
    vector<RM_Record> chosenRecords;

    RM_FileHandle *pTableHandle;
    if ((rc = db.FindTableHandle(rmp.relName, pTableHandle)))
        return rc;

    SM_Attribute *pAttr;
    if ((rc = db.FindAttribute(rmp.relName, rmp.updAttr.attrName, pAttr)))
        return rc;

    if (rmp.rhsValue.type != pAttr->attrType && rmp.rhsValue.type != NOTYPE) {
        fprintf(stderr, "QL: Update attributes with different type!\n");
        return QL_ERR_INVALID_COMMAND;
    }

    if (rmp.rhsValue.type == NOTYPE && pAttr->notNull) {
        fprintf(stderr, "QL: Update not-null attribute with NULL!\n");
        return QL_ERR_INVALID_COMMAND;
    }

    if ((rc = FindRecords(rmp.relName, chosenRecords)))
        return rc;
    
    vector<RM_Record>::iterator pRecord;
    int recordSize = pTableHandle->header.recordSize;
    for (pRecord = chosenRecords.begin(); pRecord != chosenRecords.end(); pRecord++) {
        RID rid;
        if ((rc = pRecord->GetRid(rid)))
            return rc;
        char *data;
        if ((rc = pRecord->GetData(data)))
            return rc;
        int *pNullBitmap = (int *)(data + recordSize - sizeof(int));

        // Check primary key uniqueness.
        SM_Attribute *attr = pAttr;
        if (attr->isPrimary && !attr->hasIndex) {
            RM_FileScan scan;
            if ((rc = scan.OpenScan(*pTableHandle, attr->attrType, 
                                    attr->attrLength, attr->attrOffset,
                                    attr->number,
                                    EQ_OP, rmp.rhsValue.data)))
                return rc;
            RM_Record rec;
            if (scan.GetNextRec(rec) != RM_ERR_EOF) {
                fprintf(stderr, "QL: Primary key conflicts!\n");
                scan.CloseScan();
                return QL_PRIMARY_KEY;
            }
            if ((rc = scan.CloseScan()))
                return rc;
        }

        // Check foreign key existence.
        if (attr->isForeignKey) {
            RM_FileHandle *pForeign;
            if ((rc = db.FindTableHandle(attr->refTableName, pForeign)))
                return rc;

            SM_Attribute *foreignAttr;
            if ((rc = db.FindAttribute(attr->refTableName, attr->refAttrName, foreignAttr)))
                return rc;

            if (foreignAttr->attrType != attr->attrType) {
                fprintf(stderr, "QL: mismatch attribute type with foreign key.\n");
                return rc;
            }

            RM_FileScan scan;
            if ((rc = scan.OpenScan(*pForeign, foreignAttr->attrType,
                                    foreignAttr->attrLength, foreignAttr->attrOffset,
                                    foreignAttr->number,
                                    EQ_OP, rmp.rhsValue.data)))
                return rc;

            RM_Record rec;
            if (scan.GetNextRec(rec) != 0) {
                fprintf(stderr, "QL: Foreign key does not exist!\n");
                scan.CloseScan();
                return QL_FOREIGN_KEY;
            }

            if ((rc = scan.CloseScan()))
                return rc;
        }

        Event event;
        event.type = UPDATE;
        event.record = new char[recordSize];
        event.tableName = rmp.relName;
        event.attrName = rmp.updAttr.attrName;
        memcpy(event.record, data, recordSize);

        // If indexed, remove the old entry.
        IX_IndexHandle *pIndexHandle;
        if (pAttr->hasIndex) {
            if ((rc = db.FindIndexHandle(rmp.relName, pAttr->attrName, pIndexHandle))) {
                delete[] event.record;
                return rc;
            }
            if ((rc = pIndexHandle->DeleteEntry(data + pAttr->attrOffset, rid))) {
                delete[] event.record;
                return rc;
            }
        }

        map<RID, Event, RID_LessThan>::iterator pPrevEvent = transaction.find(rid);
        if (pPrevEvent == transaction.end()) {
            transaction.insert(pair<RID, Event>(rid, event));
        } else {
            delete[] event.record;
        }

        // Modify the record.
        if (pAttr->attrType == INT || pAttr->attrType == FLOAT) {
            memcpy(data + pAttr->attrOffset, rmp.rhsValue.data, pAttr->attrLength);
            *pNullBitmap &= ~(1 << pAttr->number);
        } else if (pAttr->attrType == NOTYPE) {
            *pNullBitmap |= 1 << pAttr->number;
        } else {
            strncpy(data + pAttr->attrOffset, (const char *)rmp.rhsValue.data, pAttr->attrLength);
            *pNullBitmap &= ~(1 << pAttr->number);
        }

        // Update the record.
        if ((rc = pTableHandle->UpdateRec(*pRecord))) {
            delete[] event.record;
            return rc;
        }

        // If indexed, insert the new entry.
        if (pAttr->hasIndex) {
            if ((rc = pIndexHandle->InsertEntry(data + pAttr->attrOffset, rid))) {
                delete[] event.record;
                return rc;
            }
        }
        
    }

    return 0;
}