Exemplo n.º 1
0
 def __init__(self, blk):
     """
     Creates an iterator for the records in the log file,
     positioned after the last log record.
     This constructor is called exclusively by LogMgr.iterator()
     """
     assert isinstance(blk, Block)
     self._blk = blk
     self._pg = MaxPage()
     self._pg.read(self._blk)
     self._currentrec = self._pg.get_int(LogMgr.LAST_POS)
Exemplo n.º 2
0
class TestBasicLogRecord(TestCase):
    def setUp(self):
        SimpleDB.init_file_mgr("test")
        self.pg = MaxPage()
        self.pg.set_string(4, "Sample string")
        self.pg.set_int(0, 99999)
        self.basic_log_record = BasicLogRecord(self.pg, 0)

    def test_next_int(self):
        self.assertEqual(self.basic_log_record.next_int(), 99999)

    def test_next_string(self):
        self.basic_log_record.next_int()
        self.assertEqual(self.basic_log_record.next_string(), "Sample string")
Exemplo n.º 3
0
 def __init__(self):
     """
     Creates a new buffer, wrapping a new
     simpledb.simpledb.file.Page page.
     This constructor is called exclusively by the class BasicBufferMgr.
     It depends on the simpledb.log.LogMgr LogMgr object that it gets from the class simpledb.server.SimpleDB.
     That object is created during system initialization. Thus this constructor cannot be called until
     simpledb.server.SimpleDB.initFileAndLogMgr(String) or is called first.
     """
     self._contents = MaxPage()
     self._blk = None
     self._pins = 0
     self._modified_by = -1
     self._log_sequence_number = -1
Exemplo n.º 4
0
class TestBasicLogRecord(TestCase):

    def setUp(self):
        SimpleDB.init_file_mgr("test")
        self.pg = MaxPage()
        self.pg.set_string(4, "Sample string")
        self.pg.set_int(0, 99999)
        self.basic_log_record = BasicLogRecord(self.pg, 0)

    def test_next_int(self):
        self.assertEqual(self.basic_log_record.next_int(), 99999)

    def test_next_string(self):
        self.basic_log_record.next_int()
        self.assertEqual(self.basic_log_record.next_string(), "Sample string")
Exemplo n.º 5
0
 def next_string(self):
     """
     Returns the next value of the current log record,
     assuming it is a string.
     :return: the next value of the current log record
     """
     result = self._pg.get_string(self._pos)
     self._pos += MaxPage.str_size(len(result))
     return result
Exemplo n.º 6
0
 def __size(self, val):
     """
     Calculates the size of the specified integer or string.
     :param val: the value
     :return: the size of the value, in bytes
     """
     if isinstance(val, str):
         return MaxPage.str_size(len(val))
     else:
         return MaxPage.INT_SIZE
Exemplo n.º 7
0
 def __length_in_bytes(self, fldname):
     """
     Returns the length of a record, in bytes.
     :return: the length in bytes of a record
     """
     fldtype = self._schema.type(fldname)
     if fldtype == INTEGER:
         return MaxPage.INT_SIZE
     else:
         return MaxPage.str_size(self._schema.length(fldname))
Exemplo n.º 8
0
 def __length_in_bytes(self, fldname):
     """
     Returns the length of a record, in bytes.
     :return: the length in bytes of a record
     """
     fldtype = self._schema.type(fldname)
     if fldtype == INTEGER:
         return MaxPage.INT_SIZE
     else:
         return MaxPage.str_size(self._schema.length(fldname))
Exemplo n.º 9
0
class LogIterator:
    """
    A class that provides the ability to move through the records of the log file in reverse order.
    """

    def __init__(self, blk):
        """
        Creates an iterator for the records in the log file,
        positioned after the last log record.
        This constructor is called exclusively by LogMgr.iterator()
        """
        assert isinstance(blk, Block)
        self._blk = blk
        self._pg = MaxPage()
        self._pg.read(self._blk)
        self._currentrec = self._pg.get_int(LogMgr.LAST_POS)

    def has_next(self):
        """
        Determines if the current log record
        is the earliest record in the log file.
        :return: true if there is an earlier record
        """
        return self._currentrec > 0 or self._blk.number() > 0

    def next(self):
        """
        Moves to the next log record in reverse order.
        If the current log record is the earliest in its block,
        then the method moves to the next oldest block,
        and returns the log record from there.
        :return: the next earliest log record
        """
        if self._currentrec == 0:
            self.__move_to_next_block()
        self._currentrec = self._pg.get_int(self._currentrec)
        return BasicLogRecord(self._pg, self._currentrec + MaxPage.INT_SIZE)

    def generator(self):
        """
        To be compatible with Python's compelling features, we further implement a generator.
        This will allow us to use list comprehension
        :return: a generator of BasicLogRecord
        """
        while self.has_next():
            yield self.next()

    def __move_to_next_block(self):
        """
        Moves to the next log block in reverse order,
        and positions it after the last record in that block.
        """
        self._blk = Block(self._blk.file_name(), self._blk.number()-1)
        self._pg.read(self._blk)
        self._currentrec = self._pg.get_int(LogMgr.LAST_POS)
Exemplo n.º 10
0
 def __init__(self):
     """
     Creates a new buffer, wrapping a new
     simpledb.simpledb.file.Page page.
     This constructor is called exclusively by the class BasicBufferMgr.
     It depends on the simpledb.log.LogMgr LogMgr object that it gets from the class simpledb.server.SimpleDB.
     That object is created during system initialization. Thus this constructor cannot be called until
     simpledb.server.SimpleDB.initFileAndLogMgr(String) or is called first.
     """
     self._contents = MaxPage()
     self._blk = None
     self._pins = 0
     self._modified_by = -1
     self._log_sequence_number = -1
Exemplo n.º 11
0
 def __init__(self, logfile):
     """
     Creates the manager for the specified log file.
     If the log file does not yet exist, it is created
     with an empty first block.
     This constructor depends on a FileMgr object
     that it gets from the method simpledb.server.SimpleDB.fileMgr().
     That object is created during system initialization.
     Thus this constructor cannot be called until
     simpledb.server.SimpleDB.initFileMgr(String)
     is called first.
     :param logfile: the name of the log file
     """
     print(SimpleDB)
     self._mypage = MaxPage()
     assert isinstance(logfile, str)
     self._logfile = logfile
     logsize = SimpleDB.file_mgr().size(logfile)
     if logsize == 0:
         self.__append_new_block()
     else:
         self._currentblk = Block(logfile, logsize - 1)
         self._mypage.read(self._currentblk)
         self._currentpos = self.__get_last_record_position() + MaxPage.INT_SIZE
Exemplo n.º 12
0
 def setUp(self):
     SimpleDB.init_file_mgr("test")
     self.pg = MaxPage()
     self.pg.set_string(4, "Sample string")
     self.pg.set_int(0, 99999)
     self.basic_log_record = BasicLogRecord(self.pg, 0)
Exemplo n.º 13
0
class LogMgr:
    """
    The low-level log manager.
    This log manager is responsible for writing log records into a log file.
    A log record can be any sequence of integer and string values.
    The log manager does not understand the meaning of these values,
    which are written and read by the recovery manager
    The format of a block in a log file is like:
    pointer to the last record--------
    """
    LAST_POS = 0  # This variable is always zero.

    def __init__(self, logfile):
        """
        Creates the manager for the specified log file.
        If the log file does not yet exist, it is created
        with an empty first block.
        This constructor depends on a FileMgr object
        that it gets from the method simpledb.server.SimpleDB.fileMgr().
        That object is created during system initialization.
        Thus this constructor cannot be called until
        simpledb.server.SimpleDB.initFileMgr(String)
        is called first.
        :param logfile: the name of the log file
        """
        print(SimpleDB)
        self._mypage = MaxPage()
        assert isinstance(logfile, str)
        self._logfile = logfile
        logsize = SimpleDB.file_mgr().size(logfile)
        if logsize == 0:
            self.__append_new_block()
        else:
            self._currentblk = Block(logfile, logsize - 1)
            self._mypage.read(self._currentblk)
            self._currentpos = self.__get_last_record_position() + MaxPage.INT_SIZE

    def flush(self, lsn):
        """
        Ensures that the log records corresponding to the
        specified LSN has been written to disk.
        All earlier log records will also be written to disk.
        :param lsn: the LSN of a log record
        """
        if lsn >= self.__current_lsn():
            self.__flush()

    @synchronized
    def iterator(self):
        """
        Returns an iterator for the log records,
        which will be returned in reverse order starting with the most recent.
        """
        self.__flush()
        return LogIterator(self._currentblk)

    @synchronized
    def append(self, rec):
        """
        Appends a log record to the file.
        The record contains an arbitrary array of strings and integers.
        The method also writes an integer to the end of each log record whose value
        is the offset of the corresponding integer for the previous log record.
        These integers allow log records to be read in reverse order.
        :param rec: the list of values
        :return: the LSN of the final value
        """
        recsize = MaxPage.INT_SIZE  # 4 bytes for the integer that points to the previous log record
        for obj in rec:
            recsize += self.__size(obj)
        assert recsize <= MaxPage.BLOCK_SIZE  # Here I added a preventor
        if self._currentpos + recsize >= MaxPage.BLOCK_SIZE:  # the log record doesn't fit,
            self.__flush()  # so move to the next block.
            self.__append_new_block()  # If recsize >= BLOCK_SIZE, then BOOOOOOOOMB. XD
        for obj in rec:
            self.__append_val(obj)
        self.__finalize_record()
        return self.__current_lsn()

    def __append_val(self, val):
        """
        Adds the specified value to the page at the position denoted by currentpos.
        Then increments currentpos by the size of the value.
        :param val: the integer or string to be added to the page
        """
        if isinstance(val, str):
            self._mypage.set_string(self._currentpos, val)
        else:
            self._mypage.set_int(self._currentpos, val)
        self._currentpos += self.__size(val)

    def __size(self, val):
        """
        Calculates the size of the specified integer or string.
        :param val: the value
        :return: the size of the value, in bytes
        """
        if isinstance(val, str):
            return MaxPage.str_size(len(val))
        else:
            return MaxPage.INT_SIZE

    def __current_lsn(self):
        """
        Returns the LSN of the most recent log record.
        As implemented, the LSN is the block number where the record is stored.
        Thus every log record in a block has the same LSN.
        :return: the LSN of the most recent log record
        """
        return self._currentblk.number()

    def __flush(self):
        """
        Writes the current page to the log file.
        """
        self._mypage.write(self._currentblk)

    def __append_new_block(self):
        """
        Clear the current page, and append it to the log file.
        """
        self._mypage.clear_contents()  # The original code doesn't have this step
        self.__set_last_record_position(0)
        self._currentblk = self._mypage.append(self._logfile)
        self._currentpos = MaxPage.INT_SIZE

    def __finalize_record(self):
        """
        Sets up a circular chain of pointers to the records in the page.
        There is an integer added to the end of each log record
        whose value is the offset of the previous log record.
        """
        self._mypage.set_int(self._currentpos, self.__get_last_record_position())
        self.__set_last_record_position(self._currentpos)
        self._currentpos += MaxPage.INT_SIZE

    def __get_last_record_position(self):
        """
        The first four bytes of the page contain an integer whose value
        is the offset of the integer for the last log record in the page.
        """
        return self._mypage.get_int(LogMgr.LAST_POS)

    def __set_last_record_position(self, pos):
        self._mypage.set_int(LogMgr.LAST_POS, pos)
Exemplo n.º 14
0
 def setUp(self):
     remove_some_start_with("simple")
     SimpleDB.init_file_and_log_mgr("test")
     self.page = MaxPage()
Exemplo n.º 15
0
class BufferSlot:
    """
    An individual buffer.
    A buffer wraps a page and stores information about its status,
    such as the disk block associated with the page,
    the number of times the block has been pinned,
    whether the contents of the page have been modified,
    and if so, the id of the modifying transaction and
    the LSN of the corresponding log record.
    """

    def __init__(self):
        """
        Creates a new buffer, wrapping a new
        simpledb.simpledb.file.Page page.
        This constructor is called exclusively by the class BasicBufferMgr.
        It depends on the simpledb.log.LogMgr LogMgr object that it gets from the class simpledb.server.SimpleDB.
        That object is created during system initialization. Thus this constructor cannot be called until
        simpledb.server.SimpleDB.initFileAndLogMgr(String) or is called first.
        """
        self._contents = MaxPage()
        self._blk = None
        self._pins = 0
        self._modified_by = -1
        self._log_sequence_number = -1

    def get_int(self, offset):
        """
        Returns the integer value at the specified offset of the buffer's page.
        If an integer was not stored at that location, the behavior of the method is unpredictable.
        :param offset: the byte offset of the page
        :return: the integer value at that offset
        """
        return self._contents.get_int(offset)

    def get_string(self, offset):
        """
        Returns the string value at the specified offset of the buffer's page.
        If a string was not stored at that location, the behavior of the method is unpredictable.
        :param offset: the byte offset of the page
        :return: the string value at that offset
        """
        return self._contents.get_string(offset)

    def set_int(self, offset, val, txnum, lsn):
        """
        Writes an integer to the specified offset of the buffer's page.
        This method assumes that the transaction has already written an appropriate log record.
        The buffer saves the id of the transaction and the LSN of the log record.
        A negative lsn value indicates that a log record was not necessary.
        :param offset: the byte offset within the page
        :param val: the new integer value to be written
        :param txnum: the id of the transaction performing the modification
        :param lsn: the LSN of the corresponding log record
        """
        self._modified_by = txnum
        if lsn >= 0:
            self._log_sequence_number = lsn
        self._contents.set_int(offset, val)

    def set_string(self, offset, val, txnum, lsn):
        """
        Writes a string to the specified offset of the buffer's page.
        This method assumes that the transaction has already written an appropriate log record.
        A negative lsn value indicates that a log record was not necessary.
        The buffer saves the id of the transaction and the LSN of the log record.
        :param offset: the byte offset within the page
        :param val: the new string value to be written
        :param txnum: the id of the transaction performing the modification
        :param lsn: the LSN of the corresponding log record
        """
        assert isinstance(val, str)
        self._modified_by = txnum
        if lsn >= 0:
            self._log_sequence_number = lsn
        self._contents.set_string(offset, val)

    def block(self):
        """
        Returns a reference to the disk block that the buffer is pinned to.
        :return: a reference to a disk block
        """
        return self._blk

    def flush(self):
        """
        Writes the page to its disk block if the page is dirty.
        The method ensures that the corresponding log
        record has been written to disk prior to writing the page to disk.
        """
        if self._modified_by > 0:
            SimpleDB.log_mgr().flush(self._log_sequence_number)
            self._contents.write(self._blk)
            self._modified_by = -1

    def pin(self):
        """
        Increases the buffer's pin count.
        """
        self._pins += 1

    def unpin(self):
        """
        Decreases the buffer's pin count.
        """
        self._pins -= 1

    def is_pinned(self):
        """
        Returns true if the buffer is currently pinned (that is, if it has a nonzero pin count).
        :return: true if the buffer is pinned
        """
        return self._pins > 0

    def is_modified_by(self, txnum):
        """
        Returns true if the buffer is dirty due to a modification by the specified transaction.
        :param txnum: the id of the transaction
        :return: true if the transaction modified the buffer
        """
        return self._modified_by == txnum

    def assign_to_block(self, b):
        """
        Reads the contents of the specified block into the buffer's page.
        If the buffer was dirty, then the contents of the previous page are first written to disk.
        :param b: a reference to the data block
        """
        assert isinstance(b, Block)
        self.flush()
        self._blk = b
        self._contents.read(self._blk)
        self._pins = 0

    def assign_to_new(self, filename, fmtr):
        """
        Initializes the buffer's page according to the specified formatter,
        and appends the page to the specified file.
        If the buffer was dirty, then the contents
        of the previous page are first written to disk.
        :param filename: the name of the file
        :param fmtr: a page formatter, used to initialize the page
        """
        assert isinstance(filename, str)
        assert isinstance(fmtr, PageFormatter)
        self.flush()
        fmtr.format(self._contents)
        self._blk = self._contents.append(filename)
        self._pins = 0
Exemplo n.º 16
0
class BufferSlot:
    """
    An individual buffer.
    A buffer wraps a page and stores information about its status,
    such as the disk block associated with the page,
    the number of times the block has been pinned,
    whether the contents of the page have been modified,
    and if so, the id of the modifying transaction and
    the LSN of the corresponding log record.
    """
    def __init__(self):
        """
        Creates a new buffer, wrapping a new
        simpledb.simpledb.file.Page page.
        This constructor is called exclusively by the class BasicBufferMgr.
        It depends on the simpledb.log.LogMgr LogMgr object that it gets from the class simpledb.server.SimpleDB.
        That object is created during system initialization. Thus this constructor cannot be called until
        simpledb.server.SimpleDB.initFileAndLogMgr(String) or is called first.
        """
        self._contents = MaxPage()
        self._blk = None
        self._pins = 0
        self._modified_by = -1
        self._log_sequence_number = -1

    def get_int(self, offset):
        """
        Returns the integer value at the specified offset of the buffer's page.
        If an integer was not stored at that location, the behavior of the method is unpredictable.
        :param offset: the byte offset of the page
        :return: the integer value at that offset
        """
        return self._contents.get_int(offset)

    def get_string(self, offset):
        """
        Returns the string value at the specified offset of the buffer's page.
        If a string was not stored at that location, the behavior of the method is unpredictable.
        :param offset: the byte offset of the page
        :return: the string value at that offset
        """
        return self._contents.get_string(offset)

    def set_int(self, offset, val, txnum, lsn):
        """
        Writes an integer to the specified offset of the buffer's page.
        This method assumes that the transaction has already written an appropriate log record.
        The buffer saves the id of the transaction and the LSN of the log record.
        A negative lsn value indicates that a log record was not necessary.
        :param offset: the byte offset within the page
        :param val: the new integer value to be written
        :param txnum: the id of the transaction performing the modification
        :param lsn: the LSN of the corresponding log record
        """
        self._modified_by = txnum
        if lsn >= 0:
            self._log_sequence_number = lsn
        self._contents.set_int(offset, val)

    def set_string(self, offset, val, txnum, lsn):
        """
        Writes a string to the specified offset of the buffer's page.
        This method assumes that the transaction has already written an appropriate log record.
        A negative lsn value indicates that a log record was not necessary.
        The buffer saves the id of the transaction and the LSN of the log record.
        :param offset: the byte offset within the page
        :param val: the new string value to be written
        :param txnum: the id of the transaction performing the modification
        :param lsn: the LSN of the corresponding log record
        """
        assert isinstance(val, str)
        self._modified_by = txnum
        if lsn >= 0:
            self._log_sequence_number = lsn
        self._contents.set_string(offset, val)

    def block(self):
        """
        Returns a reference to the disk block that the buffer is pinned to.
        :return: a reference to a disk block
        """
        return self._blk

    def flush(self):
        """
        Writes the page to its disk block if the page is dirty.
        The method ensures that the corresponding log
        record has been written to disk prior to writing the page to disk.
        """
        if self._modified_by > 0:
            SimpleDB.log_mgr().flush(self._log_sequence_number)
            self._contents.write(self._blk)
            self._modified_by = -1

    def pin(self):
        """
        Increases the buffer's pin count.
        """
        self._pins += 1

    def unpin(self):
        """
        Decreases the buffer's pin count.
        """
        self._pins -= 1

    def is_pinned(self):
        """
        Returns true if the buffer is currently pinned (that is, if it has a nonzero pin count).
        :return: true if the buffer is pinned
        """
        return self._pins > 0

    def is_modified_by(self, txnum):
        """
        Returns true if the buffer is dirty due to a modification by the specified transaction.
        :param txnum: the id of the transaction
        :return: true if the transaction modified the buffer
        """
        return self._modified_by == txnum

    def assign_to_block(self, b):
        """
        Reads the contents of the specified block into the buffer's page.
        If the buffer was dirty, then the contents of the previous page are first written to disk.
        :param b: a reference to the data block
        """
        assert isinstance(b, Block)
        self.flush()
        self._blk = b
        self._contents.read(self._blk)
        self._pins = 0

    def assign_to_new(self, filename, fmtr):
        """
        Initializes the buffer's page according to the specified formatter,
        and appends the page to the specified file.
        If the buffer was dirty, then the contents
        of the previous page are first written to disk.
        :param filename: the name of the file
        :param fmtr: a page formatter, used to initialize the page
        """
        assert isinstance(filename, str)
        assert isinstance(fmtr, PageFormatter)
        self.flush()
        fmtr.format(self._contents)
        self._blk = self._contents.append(filename)
        self._pins = 0
Exemplo n.º 17
0
 def test_page(self):
     SimpleDB.init_file_mgr("test")
     page = MaxPage()
     page.set_int(0, 99999)
     page.set_string(4, "This is the sample string.")
     page.append("temp_sample_file")
     self.assertEqual(len(page._contents), 400)
     self.assertEqual(SimpleDB.fm.size("temp_sample_file"), 1)
     blk1 = Block("temp_sample_file", 1)
     page.write(blk1)
     page.clear_contents()
     page.append("temp_sample_file")
     self.assertEqual(SimpleDB.fm.size("temp_sample_file"), 3)
     blk2 = Block("temp_sample_file", 1)
     page.read(blk2)
     self.assertEqual(page.get_int(0), 99999)
     self.assertEqual(page.get_string(4), "This is the sample string.")
     self.assertEqual(page.get_int(4), 26*4)
Exemplo n.º 18
0
 def setUp(self):
     SimpleDB.init_file_mgr("test")
     self.pg = MaxPage()
     self.pg.set_string(4, "Sample string")
     self.pg.set_int(0, 99999)
     self.basic_log_record = BasicLogRecord(self.pg, 0)