Documentation
¶
Overview ¶
In postgres, page is implemented with the data layout called `slotted page`. Slotted page has slot array in page header which points to tuple(kind-of row) within the page. The figure below may be helpful to understand the structure. This is cited from postgres comment. `linp` in the figure is `slot` and the space from `pd_lower` to `pd_upper` is free space where the tuple will be inserted later.
- +----------------+---------------------------------+
- | PageHeaderData | linp1 linp2 linp3 ... |
- +-----------+----+---------------------------------+
- | ... linpN | |
- +-----------+--------------------------------------+
- | ^ pd_lower |
- | |
- | v pd_upper |
- +-------------+------------------------------------+
- | | tupleN ... |
- +-------------+------------------+-----------------+
- | ... tuple3 tuple2 tuple1 | "special space" |
- +--------------------------------+-----------------+
The structure of slotted page is mainly for the table's tuple, But this structure is also used with fsm/vm/clog/wal although slot is not used. `slot` within the structure is beneficial for mainly two reasons - the variable size of tuple can be stored
- without slot, the offset of each tuple whose size is variable cannot be identified easily while the offset can be calculated easily without slot when the size of tuple is fixed
- tuple can be moved/removed if necessary
- for example, index points to slot, not the actual location of tuple. when the tuple is moved to other location within the page, index doesn't have to be updated. just update the slot and make the slot point to the new location of tuple. this happens when vacuum removes dead tuples and compact the page
`item` is interchangeably used with `heap tuple` / `index tuple` ...
item-related interface is - GetItem(PagePtr, SlotIndex): gets item from page. the location of the item is calculated from SlotIndex's Slot - AddItem(PagePtr, ItemPtr, SlotIndex): adds item to the page. if the page does not have enough space, return error.
Page is the unit of I/O in ppdb. Disk manager organizes file as a collection of pages. Page in ppdb may be called `block“ in postgres. Page is used not only by the main disk manager but also by the clog/wal disk manager
Index ¶
- Constants
- func AddItem(page PagePtr, item ItemPtr, si SlotIndex) error
- func CalculateFileOffset(pageID PageID) int64
- func CalculateFreeSpace(page PagePtr) int
- func ClearAllVisible(p PagePtr)
- func CompactPage(page PagePtr) error
- func GetFlags(p PagePtr) uint16
- func GetLSN(p PagePtr) common.WALRecordPtr
- func GetLowerOffset(p PagePtr) offset
- func GetSpecialSpaceOffset(p PagePtr) offset
- func GetUpperOffset(p PagePtr) offset
- func InitializePage(p PagePtr, specialSpaceSize uint16)
- func IsAllVisible(p PagePtr) bool
- func IsDead(s SlotPtr) bool
- func IsInitialized(p PagePtr) bool
- func IsNormal(s SlotPtr) bool
- func IsRedirected(s SlotPtr) bool
- func IsUnused(s SlotPtr) bool
- func SetAllVisible(p PagePtr)
- func SetDead(s SlotPtr)
- func SetFlags(p PagePtr, flags uint16)
- func SetLSN(p PagePtr, lsn common.WALRecordPtr)
- func SetLowerOffset(p PagePtr, o offset)
- func SetNormal(s SlotPtr)
- func SetRedirected(s SlotPtr)
- func SetSpecialSpaceOffset(p PagePtr, o offset)
- func SetUnused(s SlotPtr)
- func SetUpperOffset(p PagePtr, o offset)
- type ItemPtr
- type PageID
- type PagePtr
- type Slot
- type SlotIndex
- type SlotPtr
Constants ¶
const ( // lower offset exported for fsm and vm LowerOffsetOffset = uint16(flagsOffset) + 2 )
byte offset of page header
const PageSize = 8192
PageSize is the byte size of page. 8KB is the default size in postgres see block_size parameter in https://www.postgresql.org/docs/current/runtime-config-preset.html
Linux OS page size is probably 4KB so torn page(partial writes) can happen. This can be avoided by full page writes (the functionality of WAL) Full page writes is probably so-called `physical logging` (not `logical logging` or `physiological logging`) see https://github.com/postgres/postgres/blob/5e7bbb528638c0f6d585bab107ec7a19e3a39deb/src/backend/storage/page/README#L36-L46
Variables ¶
This section is empty.
Functions ¶
func AddItem ¶
AddItem adds item to the page AddItem does the following - get slot index where the item will be inserted: find free slot or, when no free slot, extend new slot - generate slot data and insert it to the slot index. - insert item to the page - update page header see https://github.com/postgres/postgres/blob/2cd2569c72b8920048e35c31c9be30a6170e1410/src/backend/storage/page/bufpage.c#L194
func CalculateFileOffset ¶
CalculateFileOffset calculates the page's offset within the file the page size is fixed (8KB) so that it is easy to calculate the offset
func CalculateFreeSpace ¶
CalculateFreeSpace calculates free space within the page see: https://github.com/postgres/postgres/blob/2cd2569c72b8920048e35c31c9be30a6170e1410/src/backend/storage/page/bufpage.c#L907
func CompactPage ¶
CompactPage compacts the tuples within page. this does not compact slot.
After vacuum removes dead tuples and sets the slot flag unused, then the page can be compacted. VACUUM command does not compact slot so unused slot is unused even after VACUUM command is executed. VACUUM FULL command compacts even slot so unused slot is compacted. ex: - slot index 0, 1, 2 is used and slot 1 points to dead tuple - when VACUUM FULL command is executed, the data within slot index 2 is moved to slot index 1 - when VACUUM command is executed, the data within slot index 2 is still there slot index is called ctid in postgres, and you can confirm it like the query below - SELECT s.ctid, s.* from sample s; - VACUUM;
TODO: the logic in ppdb is not optimized, so fix this later ex: should consider whether the slots are sorted or not, and moves only tuples necessary for re-location the case slots are not sorted can happen after the page is compacted and freed slot is used when insert new tuple see https://github.com/postgres/postgres/blob/2cd2569c72b8920048e35c31c9be30a6170e1410/src/backend/storage/page/bufpage.c#L474 see https://github.com/postgres/postgres/blob/2cd2569c72b8920048e35c31c9be30a6170e1410/src/backend/storage/page/bufpage.c#L682-L699
func GetSpecialSpaceOffset ¶
func GetSpecialSpaceOffset(p PagePtr) offset
GetSpecialSpaceOffset returns special space offset
func InitializePage ¶
InitializePage initializes page when extending new page, the page is 0-filled, so should be initialized with this function see https://github.com/postgres/postgres/blob/2cd2569c72b8920048e35c31c9be30a6170e1410/src/backend/storage/page/bufpage.c#L35-L42
func IsAllVisible ¶
IsAllVisible is whether the flags allVisible is set
func IsInitialized ¶
IsInitialized checks whether the page has been already initialized when the upperOffset is 0, then the page isn't initialized see https://github.com/postgres/postgres/blob/bfcf1b34805f70df48eedeec237230d0cc1154a6/src/include/storage/bufpage.h#L231
func IsRedirected ¶
IsRedirected checks whether the page slot is redirected
func SetNormal ¶
func SetNormal(s SlotPtr)
SetNormal sets flag to normal this is expected to be used mainly when unused slot is re-used
func SetSpecialSpaceOffset ¶
func SetSpecialSpaceOffset(p PagePtr, o offset)
SetSpecialSpaceOffset sets special space offset
Types ¶
type PageID ¶
type PageID uint32
PageID is the unique identifier given to each page, which is called blockNumber in postgres see https://github.com/postgres/postgres/blob/d63d957e330c611f7a8c0ed02e4407f40f975026/src/include/storage/block.h#L17-L31
type PagePtr ¶
PagePtr is pointer to page ppdb defines page as pointer explicitly because page should not be passed by value in many cases (for concurrent access and space-efficiency) (although, using pointer here may be controversial)
func TestingNewRandomPage ¶
type Slot ¶
type Slot uint32
Slot is used for calculation or bit operation of slot basically, SlotPtr type is used instead of Slot
Slot consists of three fields - item offset/uint15. this is the offset of the item which slot points to - flag/uint2. flag indicates whether this slot is normal/unused/HOT redirected/dead - item size/uint15. this is the byte size of the item. this field is necessary because the item's length can be variable see: https://github.com/postgres/postgres/blob/27b77ecf9f4d5be211900eda54d8155ada50d696/src/include/storage/itemid.h#L17-L30
type SlotIndex ¶
type SlotIndex uint16
SlotIndex is the index of the slot within page this is not byte offset. the first slot's index is 0 and the next one's index is 1....
const ( // first slot index FirstSlotIndex SlotIndex = 0 // max slot index MaxSlotIndex SlotIndex = PageSize / slotSize // invalid slot index // maybe this invalid thing should be expressed with error type InvalidSlotIndex SlotIndex = MaxSlotIndex + 1 )
func GetNSlotIndex ¶
GetNSlotIndex returns the index of biggest page slot index which has been allocated this returns invalid slot index when no slot has been allocated