LCOV - differential code coverage report
Current view: top level - src/test/modules/test_tidstore - test_tidstore.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: bed3ffbf9d952be6c7d739d068cdce44c046dfb7 vs 574581b50ac9c63dd9e4abebb731a3b67e5b50f6 Lines: 92.2 % 141 130 11 5 125 6
Current Date: 2026-05-05 10:23:31 +0900 Functions: 100.0 % 16 16 2 14
Baseline: lcov-20260505-025707-baseline Branches: 60.0 % 80 48 32 48
Baseline Date: 2026-05-05 10:27:06 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 66.7 % 9 6 3 6
(30,360] days: 100.0 % 5 5 5
(360..) days: 93.7 % 127 119 8 119
Function coverage date bins:
(1,7] days: 100.0 % 1 1 1
(360..) days: 100.0 % 15 15 2 13
Branch coverage date bins:
(1,7] days: 25.0 % 4 1 3 1
(360..) days: 61.8 % 76 47 29 47

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*--------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * test_tidstore.c
                                  4                 :                :  *      Test TidStore data structure.
                                  5                 :                :  *
                                  6                 :                :  * Note: all locking in this test module is useless since there is only
                                  7                 :                :  * a single process to use the TidStore. It is meant to be an example of
                                  8                 :                :  * usage.
                                  9                 :                :  *
                                 10                 :                :  * Copyright (c) 2024-2026, PostgreSQL Global Development Group
                                 11                 :                :  *
                                 12                 :                :  * IDENTIFICATION
                                 13                 :                :  *      src/test/modules/test_tidstore/test_tidstore.c
                                 14                 :                :  *
                                 15                 :                :  * -------------------------------------------------------------------------
                                 16                 :                :  */
                                 17                 :                : #include "postgres.h"
                                 18                 :                : 
                                 19                 :                : #include "access/tidstore.h"
                                 20                 :                : #include "fmgr.h"
                                 21                 :                : #include "storage/block.h"
                                 22                 :                : #include "storage/itemptr.h"
                                 23                 :                : #include "storage/lwlock.h"
                                 24                 :                : #include "utils/array.h"
                                 25                 :                : #include "utils/memutils.h"
                                 26                 :                : 
  775 msawada@postgresql.o       27                 :CBC           1 : PG_MODULE_MAGIC;
                                 28                 :                : 
                                 29                 :              2 : PG_FUNCTION_INFO_V1(test_create);
                                 30                 :              2 : PG_FUNCTION_INFO_V1(do_set_block_offsets);
                                 31                 :              2 : PG_FUNCTION_INFO_V1(check_set_block_offsets);
                                 32                 :              2 : PG_FUNCTION_INFO_V1(test_is_full);
                                 33                 :              2 : PG_FUNCTION_INFO_V1(test_destroy);
                                 34                 :                : 
                                 35                 :                : static TidStore *tidstore = NULL;
                                 36                 :                : static size_t tidstore_empty_size;
                                 37                 :                : 
                                 38                 :                : /* array for verification of some tests */
                                 39                 :                : typedef struct ItemArray
                                 40                 :                : {
                                 41                 :                :     ItemPointerData *insert_tids;
                                 42                 :                :     ItemPointerData *lookup_tids;
                                 43                 :                :     ItemPointerData *iter_tids;
                                 44                 :                :     int         max_tids;
                                 45                 :                :     int         num_tids;
                                 46                 :                : } ItemArray;
                                 47                 :                : 
                                 48                 :                : static ItemArray items;
                                 49                 :                : 
                                 50                 :                : /* comparator routine for ItemPointer */
                                 51                 :                : static int
                                 52                 :         207992 : itemptr_cmp(const void *left, const void *right)
                                 53                 :                : {
                                 54                 :                :     BlockNumber lblk,
                                 55                 :                :                 rblk;
                                 56                 :                :     OffsetNumber loff,
                                 57                 :                :                 roff;
                                 58                 :                : 
   99 peter@eisentraut.org       59                 :GNC      207992 :     lblk = ItemPointerGetBlockNumber((const ItemPointerData *) left);
                                 60                 :         207992 :     rblk = ItemPointerGetBlockNumber((const ItemPointerData *) right);
                                 61                 :                : 
  775 msawada@postgresql.o       62         [ +  + ]:CBC      207992 :     if (lblk < rblk)
                                 63                 :          64084 :         return -1;
                                 64         [ +  + ]:         143908 :     if (lblk > rblk)
                                 65                 :          64510 :         return 1;
                                 66                 :                : 
   99 peter@eisentraut.org       67                 :GNC       79398 :     loff = ItemPointerGetOffsetNumber((const ItemPointerData *) left);
                                 68                 :          79398 :     roff = ItemPointerGetOffsetNumber((const ItemPointerData *) right);
                                 69                 :                : 
  775 msawada@postgresql.o       70         [ +  + ]:CBC       79398 :     if (loff < roff)
                                 71                 :          35082 :         return -1;
                                 72         [ +  + ]:          44316 :     if (loff > roff)
                                 73                 :          13842 :         return 1;
                                 74                 :                : 
                                 75                 :          30474 :     return 0;
                                 76                 :                : }
                                 77                 :                : 
                                 78                 :                : static int
    6                            79                 :          14117 : offsetnumber_cmp(const void *a, const void *b)
                                 80                 :                : {
                                 81                 :          14117 :     OffsetNumber l = *(const OffsetNumber *) a;
                                 82                 :          14117 :     OffsetNumber r = *(const OffsetNumber *) b;
                                 83                 :                : 
                                 84         [ +  - ]:          14117 :     if (l < r)
                                 85                 :          14117 :         return -1;
    6 msawada@postgresql.o       86         [ #  # ]:UBC           0 :     else if (l > r)
                                 87                 :              0 :         return 1;
                                 88                 :              0 :     return 0;
                                 89                 :                : }
                                 90                 :                : 
                                 91                 :                : /*
                                 92                 :                :  * Create a TidStore. If shared is false, the tidstore is created
                                 93                 :                :  * on TopMemoryContext, otherwise on DSA. Although the tidstore
                                 94                 :                :  * is created on DSA, only the same process can subsequently use
                                 95                 :                :  * the tidstore. The tidstore handle is not shared anywhere.
                                 96                 :                : */
                                 97                 :                : Datum
  775 msawada@postgresql.o       98                 :CBC           3 : test_create(PG_FUNCTION_ARGS)
                                 99                 :                : {
                                100                 :              3 :     bool        shared = PG_GETARG_BOOL(0);
                                101                 :                :     MemoryContext old_ctx;
                                102                 :                : 
                                103                 :                :     /* doesn't really matter, since it's just a hint */
                                104                 :              3 :     size_t      tidstore_max_size = 2 * 1024 * 1024;
                                105                 :              3 :     size_t      array_init_size = 1024;
                                106                 :                : 
                                107         [ -  + ]:              3 :     Assert(tidstore == NULL);
                                108                 :                : 
                                109                 :                :     /*
                                110                 :                :      * Create the TidStore on TopMemoryContext so that the same process use it
                                111                 :                :      * for subsequent tests.
                                112                 :                :      */
                                113                 :              3 :     old_ctx = MemoryContextSwitchTo(TopMemoryContext);
                                114                 :                : 
                                115         [ +  + ]:              3 :     if (shared)
                                116                 :                :     {
                                117                 :                :         int         tranche_id;
                                118                 :                : 
  244 nathan@postgresql.or      119                 :GNC           1 :         tranche_id = LWLockNewTrancheId("test_tidstore");
                                120                 :                : 
  768 msawada@postgresql.o      121                 :CBC           1 :         tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id);
                                122                 :                : 
                                123                 :                :         /*
                                124                 :                :          * Remain attached until end of backend or explicitly detached so that
                                125                 :                :          * the same process use the tidstore for subsequent tests.
                                126                 :                :          */
                                127                 :              1 :         dsa_pin_mapping(TidStoreGetDSA(tidstore));
                                128                 :                :     }
                                129                 :                :     else
                                130                 :                :         /* VACUUM uses insert only, so we test the other option. */
  758 john.naylor@postgres      131                 :              2 :         tidstore = TidStoreCreateLocal(tidstore_max_size, false);
                                132                 :                : 
  775 msawada@postgresql.o      133                 :              3 :     tidstore_empty_size = TidStoreMemoryUsage(tidstore);
                                134                 :                : 
                                135                 :              3 :     items.num_tids = 0;
                                136                 :              3 :     items.max_tids = array_init_size / sizeof(ItemPointerData);
                                137                 :              3 :     items.insert_tids = (ItemPointerData *) palloc0(array_init_size);
                                138                 :              3 :     items.lookup_tids = (ItemPointerData *) palloc0(array_init_size);
                                139                 :              3 :     items.iter_tids = (ItemPointerData *) palloc0(array_init_size);
                                140                 :                : 
                                141                 :              3 :     MemoryContextSwitchTo(old_ctx);
                                142                 :                : 
                                143                 :              3 :     PG_RETURN_VOID();
                                144                 :                : }
                                145                 :                : 
                                146                 :                : static void
                                147                 :           1121 : sanity_check_array(ArrayType *ta)
                                148                 :                : {
                                149   [ -  +  -  - ]:           1121 :     if (ARR_HASNULL(ta) && array_contains_nulls(ta))
  775 msawada@postgresql.o      150         [ #  # ]:UBC           0 :         ereport(ERROR,
                                151                 :                :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                152                 :                :                  errmsg("array must not contain nulls")));
                                153                 :                : 
  775 msawada@postgresql.o      154         [ -  + ]:CBC        1121 :     if (ARR_NDIM(ta) > 1)
  775 msawada@postgresql.o      155         [ #  # ]:UBC           0 :         ereport(ERROR,
                                156                 :                :                 (errcode(ERRCODE_DATA_EXCEPTION),
                                157                 :                :                  errmsg("argument must be empty or one-dimensional array")));
  775 msawada@postgresql.o      158                 :CBC        1121 : }
                                159                 :                : 
                                160                 :                : static void
  692                           161                 :           1138 : check_tidstore_available(void)
                                162                 :                : {
                                163         [ -  + ]:           1138 :     if (tidstore == NULL)
  692 msawada@postgresql.o      164         [ #  # ]:UBC           0 :         elog(ERROR, "tidstore is not created");
  692 msawada@postgresql.o      165                 :CBC        1138 : }
                                166                 :                : 
                                167                 :                : static void
  740                           168                 :           1120 : purge_from_verification_array(BlockNumber blkno)
                                169                 :                : {
                                170                 :           1120 :     int         dst = 0;
                                171                 :                : 
                                172         [ +  + ]:        3491540 :     for (int src = 0; src < items.num_tids; src++)
                                173         [ +  + ]:        3490420 :         if (ItemPointerGetBlockNumber(&items.insert_tids[src]) != blkno)
                                174                 :        3490396 :             items.insert_tids[dst++] = items.insert_tids[src];
                                175                 :           1120 :     items.num_tids = dst;
                                176                 :           1120 : }
                                177                 :                : 
                                178                 :                : 
                                179                 :                : /* Set the given block and offsets pairs */
                                180                 :                : Datum
  775                           181                 :           1121 : do_set_block_offsets(PG_FUNCTION_ARGS)
                                182                 :                : {
                                183                 :           1121 :     BlockNumber blkno = PG_GETARG_INT64(0);
                                184                 :           1121 :     ArrayType  *ta = PG_GETARG_ARRAYTYPE_P_COPY(1);
                                185                 :                :     OffsetNumber *offs;
                                186                 :                :     int         noffs;
                                187                 :                : 
  692                           188                 :           1121 :     check_tidstore_available();
  775                           189                 :           1121 :     sanity_check_array(ta);
                                190                 :                : 
                                191                 :           1121 :     noffs = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
                                192         [ -  + ]:           1121 :     offs = ((OffsetNumber *) ARR_DATA_PTR(ta));
                                193                 :                : 
                                194                 :                :     /* TidStoreSetBlockOffsets() requires offsets to be strictly ascending. */
    6                           195                 :           1121 :     qsort(offs, noffs, sizeof(OffsetNumber), offsetnumber_cmp);
                                196                 :                : 
                                197                 :                :     /* Set TIDs in the store */
  775                           198                 :           1121 :     TidStoreLockExclusive(tidstore);
                                199                 :           1121 :     TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
                                200                 :           1120 :     TidStoreUnlock(tidstore);
                                201                 :                : 
                                202                 :                :     /* Remove the existing items of blkno from the verification array */
  740                           203                 :           1120 :     purge_from_verification_array(blkno);
                                204                 :                : 
                                205                 :                :     /* Set TIDs in verification array */
  775                           206         [ +  + ]:          16357 :     for (int i = 0; i < noffs; i++)
                                207                 :                :     {
                                208                 :                :         ItemPointer tid;
                                209                 :          15237 :         int         idx = items.num_tids + i;
                                210                 :                : 
                                211                 :                :         /* Enlarge the TID arrays if necessary */
                                212         [ +  + ]:          15237 :         if (idx >= items.max_tids)
                                213                 :                :         {
                                214                 :             12 :             items.max_tids *= 2;
                                215                 :             12 :             items.insert_tids = repalloc(items.insert_tids, sizeof(ItemPointerData) * items.max_tids);
                                216                 :             12 :             items.lookup_tids = repalloc(items.lookup_tids, sizeof(ItemPointerData) * items.max_tids);
                                217                 :             12 :             items.iter_tids = repalloc(items.iter_tids, sizeof(ItemPointerData) * items.max_tids);
                                218                 :                :         }
                                219                 :                : 
                                220                 :          15237 :         tid = &(items.insert_tids[idx]);
                                221                 :          15237 :         ItemPointerSet(tid, blkno, offs[i]);
                                222                 :                :     }
                                223                 :                : 
                                224                 :                :     /* Update statistics */
                                225                 :           1120 :     items.num_tids += noffs;
                                226                 :                : 
                                227                 :           1120 :     PG_RETURN_INT64(blkno);
                                228                 :                : }
                                229                 :                : 
                                230                 :                : /*
                                231                 :                :  * Verify TIDs in store against the array.
                                232                 :                :  */
                                233                 :                : Datum
                                234                 :             12 : check_set_block_offsets(PG_FUNCTION_ARGS)
                                235                 :                : {
                                236                 :                :     TidStoreIter *iter;
                                237                 :                :     TidStoreIterResult *iter_result;
                                238                 :             12 :     int         num_iter_tids = 0;
                                239                 :             12 :     int         num_lookup_tids = 0;
  767 dgustafsson@postgres      240                 :             12 :     BlockNumber prevblkno = 0;
                                241                 :                : 
  692 msawada@postgresql.o      242                 :             12 :     check_tidstore_available();
                                243                 :                : 
                                244                 :                :     /* lookup each member in the verification array */
  775                           245         [ +  + ]:          15249 :     for (int i = 0; i < items.num_tids; i++)
                                246         [ -  + ]:          15237 :         if (!TidStoreIsMember(tidstore, &items.insert_tids[i]))
  775 msawada@postgresql.o      247         [ #  # ]:UBC           0 :             elog(ERROR, "missing TID with block %u, offset %u",
                                248                 :                :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
                                249                 :                :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
                                250                 :                : 
                                251                 :                :     /*
                                252                 :                :      * Lookup all possible TIDs for each distinct block in the verification
                                253                 :                :      * array and save successful lookups in the lookup array.
                                254                 :                :      */
                                255                 :                : 
  775 msawada@postgresql.o      256         [ +  + ]:CBC       15249 :     for (int i = 0; i < items.num_tids; i++)
                                257                 :                :     {
                                258                 :          15237 :         BlockNumber blkno = ItemPointerGetBlockNumber(&items.insert_tids[i]);
                                259                 :                : 
                                260   [ +  +  +  + ]:          15237 :         if (i > 0 && blkno == prevblkno)
                                261                 :          14117 :             continue;
                                262                 :                : 
                                263         [ +  + ]:        2293760 :         for (OffsetNumber offset = FirstOffsetNumber; offset < MaxOffsetNumber; offset++)
                                264                 :                :         {
                                265                 :                :             ItemPointerData tid;
                                266                 :                : 
                                267                 :        2292640 :             ItemPointerSet(&tid, blkno, offset);
                                268                 :                : 
                                269                 :        2292640 :             TidStoreLockShare(tidstore);
                                270         [ +  + ]:        2292640 :             if (TidStoreIsMember(tidstore, &tid))
                                271                 :          15237 :                 ItemPointerSet(&items.lookup_tids[num_lookup_tids++], blkno, offset);
                                272                 :        2292640 :             TidStoreUnlock(tidstore);
                                273                 :                :         }
                                274                 :                : 
                                275                 :           1120 :         prevblkno = blkno;
                                276                 :                :     }
                                277                 :                : 
                                278                 :                :     /* Collect TIDs stored in the tidstore, in order */
                                279                 :                : 
                                280                 :             12 :     TidStoreLockShare(tidstore);
                                281                 :             12 :     iter = TidStoreBeginIterate(tidstore);
                                282         [ +  + ]:           1132 :     while ((iter_result = TidStoreIterateNext(iter)) != NULL)
                                283                 :                :     {
                                284                 :                :         OffsetNumber offsets[MaxOffsetNumber];
                                285                 :                :         int         num_offsets;
                                286                 :                : 
  650 tmunro@postgresql.or      287                 :           1120 :         num_offsets = TidStoreGetBlockOffsets(iter_result, offsets, lengthof(offsets));
                                288         [ -  + ]:           1120 :         Assert(num_offsets <= lengthof(offsets));
                                289         [ +  + ]:          16357 :         for (int i = 0; i < num_offsets; i++)
  775 msawada@postgresql.o      290                 :          15237 :             ItemPointerSet(&(items.iter_tids[num_iter_tids++]), iter_result->blkno,
  650 tmunro@postgresql.or      291                 :          15237 :                            offsets[i]);
                                292                 :                :     }
  775 msawada@postgresql.o      293                 :             12 :     TidStoreEndIterate(iter);
                                294                 :             12 :     TidStoreUnlock(tidstore);
                                295                 :                : 
                                296                 :                :     /*
                                297                 :                :      * Sort verification and lookup arrays and test that all arrays are the
                                298                 :                :      * same.
                                299                 :                :      */
                                300                 :                : 
                                301         [ -  + ]:             12 :     if (num_lookup_tids != items.num_tids)
  775 msawada@postgresql.o      302         [ #  # ]:UBC           0 :         elog(ERROR, "should have %d TIDs, have %d", items.num_tids, num_lookup_tids);
  775 msawada@postgresql.o      303         [ -  + ]:CBC          12 :     if (num_iter_tids != items.num_tids)
  775 msawada@postgresql.o      304         [ #  # ]:UBC           0 :         elog(ERROR, "should have %d TIDs, have %d", items.num_tids, num_iter_tids);
                                305                 :                : 
  775 msawada@postgresql.o      306                 :CBC          12 :     qsort(items.insert_tids, items.num_tids, sizeof(ItemPointerData), itemptr_cmp);
                                307                 :             12 :     qsort(items.lookup_tids, items.num_tids, sizeof(ItemPointerData), itemptr_cmp);
                                308         [ +  + ]:          15249 :     for (int i = 0; i < items.num_tids; i++)
                                309                 :                :     {
  515 peter@eisentraut.org      310         [ -  + ]:          15237 :         if (itemptr_cmp(&items.insert_tids[i], &items.iter_tids[i]) != 0)
  775 msawada@postgresql.o      311         [ #  # ]:UBC           0 :             elog(ERROR, "TID iter array doesn't match verification array, got (%u,%u) expected (%u,%u)",
                                312                 :                :                  ItemPointerGetBlockNumber(&items.iter_tids[i]),
                                313                 :                :                  ItemPointerGetOffsetNumber(&items.iter_tids[i]),
                                314                 :                :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
                                315                 :                :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
  515 peter@eisentraut.org      316         [ -  + ]:CBC       15237 :         if (itemptr_cmp(&items.insert_tids[i], &items.lookup_tids[i]) != 0)
  775 msawada@postgresql.o      317         [ #  # ]:UBC           0 :             elog(ERROR, "TID lookup array doesn't match verification array, got (%u,%u) expected (%u,%u)",
                                318                 :                :                  ItemPointerGetBlockNumber(&items.lookup_tids[i]),
                                319                 :                :                  ItemPointerGetOffsetNumber(&items.lookup_tids[i]),
                                320                 :                :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
                                321                 :                :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
                                322                 :                :     }
                                323                 :                : 
  775 msawada@postgresql.o      324                 :CBC          12 :     PG_RETURN_VOID();
                                325                 :                : }
                                326                 :                : 
                                327                 :                : /*
                                328                 :                :  * In real world use, we care if the memory usage is greater than
                                329                 :                :  * some configured limit. Here we just want to verify that
                                330                 :                :  * TidStoreMemoryUsage is not broken.
                                331                 :                :  */
                                332                 :                : Datum
                                333                 :              2 : test_is_full(PG_FUNCTION_ARGS)
                                334                 :                : {
                                335                 :                :     bool        is_full;
                                336                 :                : 
  692                           337                 :              2 :     check_tidstore_available();
                                338                 :                : 
  775                           339                 :              2 :     is_full = (TidStoreMemoryUsage(tidstore) > tidstore_empty_size);
                                340                 :                : 
                                341                 :              2 :     PG_RETURN_BOOL(is_full);
                                342                 :                : }
                                343                 :                : 
                                344                 :                : /* Free the tidstore */
                                345                 :                : Datum
                                346                 :              3 : test_destroy(PG_FUNCTION_ARGS)
                                347                 :                : {
  692                           348                 :              3 :     check_tidstore_available();
                                349                 :                : 
  775                           350                 :              3 :     TidStoreDestroy(tidstore);
                                351                 :              3 :     tidstore = NULL;
                                352                 :              3 :     items.num_tids = 0;
                                353                 :              3 :     pfree(items.insert_tids);
                                354                 :              3 :     pfree(items.lookup_tids);
                                355                 :              3 :     pfree(items.iter_tids);
                                356                 :                : 
                                357                 :              3 :     PG_RETURN_VOID();
                                358                 :                : }
        

Generated by: LCOV version 2.5.0-beta