Read up to n items (bytes/chars) from the lob and return them.
        If n is -1 then all available data is returned.
        Might trigger further loading of data from the database if the number of items requested for
        reading is larger than what is currently buffered.

def read(self, n=-1):
        """Read up to n items (bytes/chars) from the lob and return them.
        If n is -1 then all available data is returned.
        Might trigger further loading of data from the database if the number of items requested for
        reading is larger than what is currently buffered.
        """
        pos = self.tell()
        num_items_to_read = n if n != -1 else self.length - pos
        # calculate the position of the file pointer after data was read:
        new_pos = min(pos + num_items_to_read, self.length)

        if new_pos > self._current_lob_length:
            missing_num_items_to_read = new_pos - self._current_lob_length
            self._read_missing_lob_data_from_db(self._current_lob_length, missing_num_items_to_read)
        # reposition file pointer to original position as reading in IO buffer might have changed it
        self.seek(pos, SEEK_SET)
        return self.data.read(n)