class XidLruCache: PAGES_PER_SEGMENT = 32 def __init__(self, datadir, pageclass, buffsz): self.datadir = datadir ctrlfile = ControlFile(datadir) self.blcksz = ctrlfile.blcksz self.buffer = LruCache(pageclass, self.blcksz, buffsz) def getfilename(self, segno): pass def readfromdisk(self, pageno): blocksz = self.blcksz segno = pageno / self.PAGES_PER_SEGMENT filename = self.getfilename(segno) try: with open(filename) as file: file.seek(blocksz * pageno) block = file.read(blocksz) except: raise UPgException('error in reading xid') if len(block) != blocksz: raise UPgException('error in reading xid') return block def getlrupage(self, pageno): lrupage = self.buffer.get_and_visit(pageno) if not lrupage: block = self.readfromdisk(pageno) lrupage = self.buffer.put(pageno, block) return lrupage
class HeapBuffer: def __init__(self, pgdatadir, catalog_class): self.pgdatadir = pgdatadir self.catalog_class = catalog_class ctrlfile = ControlFile(pgdatadir) self.blocksz = ctrlfile.blcksz self.segfilesz = ctrlfile.relseg_size self.catalog_version_no = ctrlfile.catalog_version_no self.pg_majorversion = catalog_class.loadpgversion(pgdatadir) self.heapbuffer = LruCache(HeapBufferPage, self.blocksz, HEAP_BUFFER_COUNT) ''' 计算buffer tag。 ''' def __getbuftag(self, relfilenode, forknum, blocknum): return struct.pack('5I', relfilenode.space_node, relfilenode.db_node, relfilenode.rel_node, forknum, blocknum) ''' 根据relfilenode得到文件名。根据global tablespace,default table space,还有指定了table space,文件名 规则不一样。 ''' def getrelationpath(self, relfilenode, forknum): assert forknum == MAIN_FORKNUM or forknum == FSM_FORKNUM \ or forknum == VISIBILITYMAP_FORKNUM or forknum == INIT_FORKNUM pgdatadir = self.pgdatadir forkNames = ('', 'fsm', 'vm', 'init') if forknum == MAIN_FORKNUM: filename = relfilenode.rel_node else: filename = '%u_%s' % (relfilenode.rel_node, forkNames[forknum]) if relfilenode.space_node == self.catalog_class.GLOBALTABLESPACE_OID: return '%s/global/%s' % (pgdatadir, filename) elif relfilenode.space_node == self.catalog_class.DEFAULTTABLESPACE_OID: return '%s/base/%u/%s' % (pgdatadir, relfilenode.db_node, filename) else: tablespacedir = "PG_%s_%u" % (self.pg_majorversion, self.catalog_version_no) return '%s/pg_tblspc/%u/%s/%u/%s' % ( pgdatadir, relfilenode.space_node, tablespacedir, relfilenode.db_node, filename) def __loadbuffer(self, relfilenode, forknum, blocknum): try: blocksz = self.blocksz segsz = self.segfilesz filepath = self.getrelationpath(relfilenode, forknum) segno = blocknum / segsz if segno > 0: filepath = '%s.%u' % (filepath, segno) blockoff = blocksz * (blocknum % segsz) with open(filepath) as file: file.seek(blockoff) block = file.read(blocksz) if len(block) != blocksz: logger.error('could not read block %u in file \"%s\": %m' % (blocknum, filepath)) raise UPgException('could not read block in file') return block except IOError: logger.error('could not read block %u in file \"%s\": %m' % (blocknum, filepath)) raise UPgException('could not read block in file') def readpage(self, relfilenode, forknum, blocknum): tag = self.__getbuftag(relfilenode, forknum, blocknum) buffpage = self.heapbuffer.get_and_visit(tag) if not buffpage: buffdata = self.__loadbuffer(relfilenode, forknum, blocknum) buffpage = self.heapbuffer.put(tag, buffdata) return buffpage def getblocknums(self, relfilenode, forknum): blocksz = self.blocksz segsz = self.segfilesz filepath = self.getrelationpath(relfilenode, forknum) segno = 0 while True: if segno > 0: filepath = '%s.%u' % (filepath, segno) if not os.path.exists(filepath): return segno * segsz filesize = os.path.getsize(filepath) / blocksz if filesize > segsz: raise UPgException('could not read block in file') elif filesize < segsz: return segno * segsz + filesize else: segno += 1
class HeapBuffer: def __init__(self, pgdatadir, catalog_class): self.pgdatadir = pgdatadir self.catalog_class = catalog_class ctrlfile = ControlFile(pgdatadir) self.blocksz = ctrlfile.blcksz self.segfilesz = ctrlfile.relseg_size self.catalog_version_no = ctrlfile.catalog_version_no self.pg_majorversion = catalog_class.loadpgversion(pgdatadir) self.heapbuffer = LruCache(HeapBufferPage, self.blocksz, HEAP_BUFFER_COUNT) ''' 计算buffer tag。 ''' def __getbuftag(self, relfilenode, forknum, blocknum): return struct.pack('5I', relfilenode.space_node, relfilenode.db_node, relfilenode.rel_node, forknum, blocknum) ''' 根据relfilenode得到文件名。根据global tablespace,default table space,还有指定了table space,文件名 规则不一样。 ''' def getrelationpath(self, relfilenode, forknum): assert forknum == MAIN_FORKNUM or forknum == FSM_FORKNUM \ or forknum == VISIBILITYMAP_FORKNUM or forknum == INIT_FORKNUM pgdatadir = self.pgdatadir forkNames = ('', 'fsm', 'vm', 'init') if forknum == MAIN_FORKNUM: filename = relfilenode.rel_node else: filename = '%u_%s'%(relfilenode.rel_node, forkNames[forknum]) if relfilenode.space_node == self.catalog_class.GLOBALTABLESPACE_OID: return '%s/global/%s'%(pgdatadir, filename) elif relfilenode.space_node == self.catalog_class.DEFAULTTABLESPACE_OID: return '%s/base/%u/%s'%(pgdatadir, relfilenode.db_node, filename) else: tablespacedir = "PG_%s_%u"%(self.pg_majorversion, self.catalog_version_no) return '%s/pg_tblspc/%u/%s/%u/%s'%(pgdatadir, relfilenode.space_node, tablespacedir, relfilenode.db_node, filename) def __loadbuffer(self, relfilenode, forknum, blocknum): try: blocksz = self.blocksz segsz = self.segfilesz filepath = self.getrelationpath(relfilenode, forknum) segno = blocknum/segsz if segno > 0: filepath = '%s.%u'%(filepath, segno) blockoff = blocksz * (blocknum%segsz) with open(filepath) as file: file.seek(blockoff) block = file.read(blocksz) if len(block) != blocksz: logger.error('could not read block %u in file \"%s\": %m'%(blocknum, filepath)) raise UPgException('could not read block in file') return block except IOError: logger.error('could not read block %u in file \"%s\": %m'%(blocknum, filepath)) raise UPgException('could not read block in file') def readpage(self, relfilenode, forknum, blocknum): tag = self.__getbuftag(relfilenode, forknum, blocknum) buffpage = self.heapbuffer.get_and_visit(tag) if not buffpage: buffdata = self.__loadbuffer(relfilenode, forknum, blocknum) buffpage = self.heapbuffer.put(tag, buffdata) return buffpage def getblocknums(self, relfilenode, forknum): blocksz = self.blocksz segsz = self.segfilesz filepath = self.getrelationpath(relfilenode, forknum) segno = 0 while True: if segno > 0: filepath = '%s.%u' % (filepath, segno) if not os.path.exists(filepath): return segno * segsz filesize = os.path.getsize(filepath)/blocksz if filesize > segsz: raise UPgException('could not read block in file') elif filesize < segsz: return segno * segsz + filesize else: segno += 1