5.3. Inserting, Deleting and Updating Tuples

This section describes how tuples are inserted, deleted, and updated. Then, the Free Space Map (FSM), which is used to insert and update tuples, is briefly described.

To focus on tuples, page headers and line pointers are not represented in the following. Figure 5.3 shows an example of how tuples are represented.

Fig. 5.3. Representation of tuples.

5.3.1. Insertion

With the insertion operation, a new tuple is inserted directly into a page of the target table (Fig. 5.4).

Fig. 5.4. Tuple insertion.

Suppose that a tuple is inserted in a page by a transaction whose txid is 99. In this case, the header fields of the inserted tuple are set as follows.

  • Tuple_1:
    • t_xmin is set to 99 because this tuple is inserted by txid 99.
    • t_xmax is set to 0 because this tuple has not been deleted or updated.
    • t_cid is set to 0 because this tuple is the first tuple inserted by txid 99.
    • t_ctid is set to (0,1), which points to itself, because this is the latest tuple.

PostgreSQL provides the ‘pageinspect’ extension, which is a contribution module, to show the contents of the database pages.

testdb=# CREATE EXTENSION pageinspect;
testdb=# CREATE TABLE tbl (data text);
testdb=# INSERT INTO tbl VALUES('A');
testdb=# SELECT lp as tuple, t_xmin, t_xmax, t_field3 as t_cid, t_ctid 
                FROM heap_page_items(get_raw_page('tbl', 0));
 tuple | t_xmin | t_xmax | t_cid | t_ctid 
     1 |     99 |      0 |     0 | (0,1)
(1 row)

5.3.2. Deletion

In the deletion operation, the target tuple is deleted logically. The value of the txid that executes the DELETE command is set to the t_xmax of the tuple (Fig. 5.5).

Fig. 5.5. Tuple deletion.

Suppose that tuple Tuple_1 is deleted by txid 111. In this case, the header fields of Tuple_1 are set as follows:

  • Tuple_1:
    • t_xmax is set to 111.

If txid 111 is committed, Tuple_1 is no longer required. Generally, unneeded tuples are referred to as dead tuples in PostgreSQL.

Dead tuples should eventually be removed from pages. Cleaning dead tuples is referred to as VACUUM processing, which is described in Chapter 6.

5.3.3. Update

In the update operation, PostgreSQL logically deletes the latest tuple and inserts a new one (Fig. 5.6).

Fig. 5.6. Update the row twice.

Suppose that the row, which has been inserted by txid 99, is updated twice by txid 100.

When the first UPDATE command is executed, Tuple_1 is logically deleted by setting txid 100 to the t_xmax, and then Tuple_2 is inserted. Then, the t_ctid of Tuple_1 is rewritten to point to Tuple_2. The header fields of both Tuple_1 and Tuple_2 are as follows:

  • Tuple_1:

    • t_xmax is set to 100.
    • t_ctid is rewritten from (0, 1) to (0, 2).
  • Tuple_2:

    • t_xmin is set to 100.
    • t_xmax is set to 0.
    • t_cid is set to 0.
    • t_ctid is set to (0,2).

When the second UPDATE command is executed, as in the first UPDATE command, Tuple_2 is logically deleted and Tuple_3 is inserted. The header fields of both Tuple_2 and Tuple_3 are as follows:

  • Tuple_2:

    • t_xmax is set to 100.
    • t_ctid is rewritten from (0, 2) to (0, 3).
  • Tuple_3:

    • t_xmin is set to 100.
    • t_xmax is set to 0.
    • t_cid is set to 1.
    • t_ctid is set to (0,3).

As with the delete operation, if txid 100 is committed, Tuple_1 and Tuple_2 will be dead tuples, and, if txid 100 is aborted, Tuple_2 and Tuple_3 will be dead tuples.

5.3.4. Free Space Map

When inserting a heap or an index tuple, PostgreSQL uses the FSM of the corresponding table or index to select the page which can be inserted into.

As mentioned in Section 1.2.3, all tables and indexes have respective FSMs. Each FSM stores the information about the free space capacity of each page within the corresponding table or index file.

All FSMs are stored with the suffix ‘fsm’, and they are loaded into shared memory if necessary.


The extension pg_freespacemap provides the freespace of the specified table/index. The following query shows the freespace ratio of each page in the specified table.

testdb=# CREATE EXTENSION pg_freespacemap;

testdb=# SELECT *, round(100 * avail/8192 ,2) as "freespace ratio"
                FROM pg_freespace('accounts');
 blkno | avail | freespace ratio 
     0 |  7904 |           96.00
     1 |  7520 |           91.00
     2 |  7136 |           87.00
     3 |  7136 |           87.00
     4 |  7136 |           87.00
     5 |  7136 |           87.00