Explore Table Access Method Capabilities: Sequential Scan Analyzed

Enterprise PostgreSQL Solutions

Leave a Comment

Explore Table Access Method Capabilities: Sequential Scan Analyzed


In my previous blog about table access method here, we discussed the basis of PostgreSQL’s table access method APIs and the difference between a heap tuple and Tuple Table Slot (TTS). In this blog, let’s talk more about the particular API calls that helps PostgreSQL core to achieve sequential scan.

APIs Involved

To achieve a sequential scan, the following table access method API callbacks will be involved:

  • relation_size()
  • slot_callbacks()
  • scan_begin()
  • scan_getnextslot()
  • scan_end()

By default, PostgreSQL uses heap access method, and it implements the above APIs as:

  • table_block_relation_size()
  • heapam_slot_callbacks()
  • heap_beginscan()
  • heap_getnextslot()
  • heap_endscan()

these are located in src/backend/access/heap/heapam_handler.c.

Basic Workflow

API Function Calls

table_block_relation_size(Relation rel, ForkNumber forkNumber):

This is the first function to be called during the query planning phase and it is supposed to physically examine the size of the given Relation rel and ForkNumber forkNumber. It shall returns the total number of blocks (8KB size by default) to the caller so that it can plan the best query strategy based on how large of a table we are working with.

What is a relation?

A relation, in most cases, represents a table, an index or a view. In our case, it represents a table and it contains important information about this table, such as the object identifiers (OIDs) for database, table space, number of attributes, other flags…etc

What is a fork number?

A relation is composed of multiple forks

  • MAIN_FORKNUM (0: stores user data)
  • FSM_FORKNUM (1: stores free space map data)
  • VISIBILITY_FORKNUM (2: visibility data)
  • INIT_FORKNUM (3: used to reset WAL-logged table)

more on fork number here


This function is called to return an appropriate TTS Operations so executor is able to convert a heap tuple to Tuple Table Slot (TTS). For heap access method, it uses this TTS callback with consideration of buffer manager:

const TupleTableSlotOps TTSOpsBufferHeapTuple = {
	.base_slot_size = sizeof(BufferHeapTupleTableSlot),
	.init = tts_buffer_heap_init,
	.release = tts_buffer_heap_release,
	.clear = tts_buffer_heap_clear,
	.getsomeattrs = tts_buffer_heap_getsomeattrs,
	.getsysattr = tts_buffer_heap_getsysattr,
	.materialize = tts_buffer_heap_materialize,
	.copyslot = tts_buffer_heap_copyslot,
	.get_heap_tuple = tts_buffer_heap_get_heap_tuple,

	/* A buffer heap tuple table slot can not "own" a minimal tuple. */
	.get_minimal_tuple = NULL,
	.copy_heap_tuple = tts_buffer_heap_copy_heap_tuple,
	.copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple

scan_begin() and scan_end()

Called before and after the actual sequential scan. scan_begin is responsible for initializing a HeapScanDesc that describes the status of current status, including values such as: current block number, total number of blocks to be scanned, scan mode, snapshot…etc. The definition can be located in src/include/backend/access/heapam.h.

Scan_end(), on the other hand, cleans up the HeapScanDesc allocated in scan_begin()


Core of sequential scan. This function is responsible to retrieve a tuple from storage engine. In PostgreSQL, it requests a tuple from buffer manager module, which can either read the tuple from its own memory buffer or read it from physical storage.

This is why it is possible to create your own database storage engine with this API, It is possible to read and write tuples purely from and to memory to form an in-memory database.

If you still have data to scan, this function needs to return true, so executor will call it again. If all the data has been exhausted, it returns false to indicate the end of scan.


This is the basic workflow of a sequential scan in PostgreSQL, which utilizes the table access method APIs. In the next blog, we will explore other APIs to handle other DML commands such as insert, update, delete, truncate..etc.