LCOV - code coverage report
Current view: top level - src/test/modules/test_tidstore - test_tidstore.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 123 131 93.9 %
Date: 2024-11-21 08:14:44 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          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, 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             : 
      27           2 : PG_MODULE_MAGIC;
      28             : 
      29           4 : PG_FUNCTION_INFO_V1(test_create);
      30           4 : PG_FUNCTION_INFO_V1(do_set_block_offsets);
      31           4 : PG_FUNCTION_INFO_V1(check_set_block_offsets);
      32           4 : PG_FUNCTION_INFO_V1(test_is_full);
      33           4 : 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      416168 : itemptr_cmp(const void *left, const void *right)
      53             : {
      54             :     BlockNumber lblk,
      55             :                 rblk;
      56             :     OffsetNumber loff,
      57             :                 roff;
      58             : 
      59      416168 :     lblk = ItemPointerGetBlockNumber((ItemPointer) left);
      60      416168 :     rblk = ItemPointerGetBlockNumber((ItemPointer) right);
      61             : 
      62      416168 :     if (lblk < rblk)
      63      128168 :         return -1;
      64      288000 :     if (lblk > rblk)
      65      129020 :         return 1;
      66             : 
      67      158980 :     loff = ItemPointerGetOffsetNumber((ItemPointer) left);
      68      158980 :     roff = ItemPointerGetOffsetNumber((ItemPointer) right);
      69             : 
      70      158980 :     if (loff < roff)
      71       70256 :         return -1;
      72       88724 :     if (loff > roff)
      73       27684 :         return 1;
      74             : 
      75       61040 :     return 0;
      76             : }
      77             : 
      78             : /*
      79             :  * Create a TidStore. If shared is false, the tidstore is created
      80             :  * on TopMemoryContext, otherwise on DSA. Although the tidstore
      81             :  * is created on DSA, only the same process can subsequently use
      82             :  * the tidstore. The tidstore handle is not shared anywhere.
      83             : */
      84             : Datum
      85           6 : test_create(PG_FUNCTION_ARGS)
      86             : {
      87           6 :     bool        shared = PG_GETARG_BOOL(0);
      88             :     MemoryContext old_ctx;
      89             : 
      90             :     /* doesn't really matter, since it's just a hint */
      91           6 :     size_t      tidstore_max_size = 2 * 1024 * 1024;
      92           6 :     size_t      array_init_size = 1024;
      93             : 
      94             :     Assert(tidstore == NULL);
      95             : 
      96             :     /*
      97             :      * Create the TidStore on TopMemoryContext so that the same process use it
      98             :      * for subsequent tests.
      99             :      */
     100           6 :     old_ctx = MemoryContextSwitchTo(TopMemoryContext);
     101             : 
     102           6 :     if (shared)
     103             :     {
     104             :         int         tranche_id;
     105             : 
     106           2 :         tranche_id = LWLockNewTrancheId();
     107           2 :         LWLockRegisterTranche(tranche_id, "test_tidstore");
     108             : 
     109           2 :         tidstore = TidStoreCreateShared(tidstore_max_size, tranche_id);
     110             : 
     111             :         /*
     112             :          * Remain attached until end of backend or explicitly detached so that
     113             :          * the same process use the tidstore for subsequent tests.
     114             :          */
     115           2 :         dsa_pin_mapping(TidStoreGetDSA(tidstore));
     116             :     }
     117             :     else
     118             :         /* VACUUM uses insert only, so we test the other option. */
     119           4 :         tidstore = TidStoreCreateLocal(tidstore_max_size, false);
     120             : 
     121           6 :     tidstore_empty_size = TidStoreMemoryUsage(tidstore);
     122             : 
     123           6 :     items.num_tids = 0;
     124           6 :     items.max_tids = array_init_size / sizeof(ItemPointerData);
     125           6 :     items.insert_tids = (ItemPointerData *) palloc0(array_init_size);
     126           6 :     items.lookup_tids = (ItemPointerData *) palloc0(array_init_size);
     127           6 :     items.iter_tids = (ItemPointerData *) palloc0(array_init_size);
     128             : 
     129           6 :     MemoryContextSwitchTo(old_ctx);
     130             : 
     131           6 :     PG_RETURN_VOID();
     132             : }
     133             : 
     134             : static void
     135        2242 : sanity_check_array(ArrayType *ta)
     136             : {
     137        2242 :     if (ARR_HASNULL(ta) && array_contains_nulls(ta))
     138           0 :         ereport(ERROR,
     139             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     140             :                  errmsg("array must not contain nulls")));
     141             : 
     142        2242 :     if (ARR_NDIM(ta) > 1)
     143           0 :         ereport(ERROR,
     144             :                 (errcode(ERRCODE_DATA_EXCEPTION),
     145             :                  errmsg("argument must be empty or one-dimensional array")));
     146        2242 : }
     147             : 
     148             : static void
     149        2276 : check_tidstore_available(void)
     150             : {
     151        2276 :     if (tidstore == NULL)
     152           0 :         elog(ERROR, "tidstore is not created");
     153        2276 : }
     154             : 
     155             : static void
     156        2240 : purge_from_verification_array(BlockNumber blkno)
     157             : {
     158        2240 :     int         dst = 0;
     159             : 
     160     6986506 :     for (int src = 0; src < items.num_tids; src++)
     161     6984266 :         if (ItemPointerGetBlockNumber(&items.insert_tids[src]) != blkno)
     162     6984218 :             items.insert_tids[dst++] = items.insert_tids[src];
     163        2240 :     items.num_tids = dst;
     164        2240 : }
     165             : 
     166             : 
     167             : /* Set the given block and offsets pairs */
     168             : Datum
     169        2242 : do_set_block_offsets(PG_FUNCTION_ARGS)
     170             : {
     171        2242 :     BlockNumber blkno = PG_GETARG_INT64(0);
     172        2242 :     ArrayType  *ta = PG_GETARG_ARRAYTYPE_P_COPY(1);
     173             :     OffsetNumber *offs;
     174             :     int         noffs;
     175             : 
     176        2242 :     check_tidstore_available();
     177        2242 :     sanity_check_array(ta);
     178             : 
     179        2242 :     noffs = ArrayGetNItems(ARR_NDIM(ta), ARR_DIMS(ta));
     180        2242 :     offs = ((OffsetNumber *) ARR_DATA_PTR(ta));
     181             : 
     182             :     /* Set TIDs in the store */
     183        2242 :     TidStoreLockExclusive(tidstore);
     184        2242 :     TidStoreSetBlockOffsets(tidstore, blkno, offs, noffs);
     185        2240 :     TidStoreUnlock(tidstore);
     186             : 
     187             :     /* Remove the existing items of blkno from the verification array */
     188        2240 :     purge_from_verification_array(blkno);
     189             : 
     190             :     /* Set TIDs in verification array */
     191       32760 :     for (int i = 0; i < noffs; i++)
     192             :     {
     193             :         ItemPointer tid;
     194       30520 :         int         idx = items.num_tids + i;
     195             : 
     196             :         /* Enlarge the TID arrays if necessary */
     197       30520 :         if (idx >= items.max_tids)
     198             :         {
     199          24 :             items.max_tids *= 2;
     200          24 :             items.insert_tids = repalloc(items.insert_tids, sizeof(ItemPointerData) * items.max_tids);
     201          24 :             items.lookup_tids = repalloc(items.lookup_tids, sizeof(ItemPointerData) * items.max_tids);
     202          24 :             items.iter_tids = repalloc(items.iter_tids, sizeof(ItemPointerData) * items.max_tids);
     203             :         }
     204             : 
     205       30520 :         tid = &(items.insert_tids[idx]);
     206       30520 :         ItemPointerSet(tid, blkno, offs[i]);
     207             :     }
     208             : 
     209             :     /* Update statistics */
     210        2240 :     items.num_tids += noffs;
     211             : 
     212        2240 :     PG_RETURN_INT64(blkno);
     213             : }
     214             : 
     215             : /*
     216             :  * Verify TIDs in store against the array.
     217             :  */
     218             : Datum
     219          24 : check_set_block_offsets(PG_FUNCTION_ARGS)
     220             : {
     221             :     TidStoreIter *iter;
     222             :     TidStoreIterResult *iter_result;
     223          24 :     int         num_iter_tids = 0;
     224          24 :     int         num_lookup_tids = 0;
     225          24 :     BlockNumber prevblkno = 0;
     226             : 
     227          24 :     check_tidstore_available();
     228             : 
     229             :     /* lookup each member in the verification array */
     230       30544 :     for (int i = 0; i < items.num_tids; i++)
     231       30520 :         if (!TidStoreIsMember(tidstore, &items.insert_tids[i]))
     232           0 :             elog(ERROR, "missing TID with block %u, offset %u",
     233             :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
     234             :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
     235             : 
     236             :     /*
     237             :      * Lookup all possible TIDs for each distinct block in the verification
     238             :      * array and save successful lookups in the lookup array.
     239             :      */
     240             : 
     241       30544 :     for (int i = 0; i < items.num_tids; i++)
     242             :     {
     243       30520 :         BlockNumber blkno = ItemPointerGetBlockNumber(&items.insert_tids[i]);
     244             : 
     245       30520 :         if (i > 0 && blkno == prevblkno)
     246       28280 :             continue;
     247             : 
     248     4587520 :         for (OffsetNumber offset = FirstOffsetNumber; offset < MaxOffsetNumber; offset++)
     249             :         {
     250             :             ItemPointerData tid;
     251             : 
     252     4585280 :             ItemPointerSet(&tid, blkno, offset);
     253             : 
     254     4585280 :             TidStoreLockShare(tidstore);
     255     4585280 :             if (TidStoreIsMember(tidstore, &tid))
     256       30520 :                 ItemPointerSet(&items.lookup_tids[num_lookup_tids++], blkno, offset);
     257     4585280 :             TidStoreUnlock(tidstore);
     258             :         }
     259             : 
     260        2240 :         prevblkno = blkno;
     261             :     }
     262             : 
     263             :     /* Collect TIDs stored in the tidstore, in order */
     264             : 
     265          24 :     TidStoreLockShare(tidstore);
     266          24 :     iter = TidStoreBeginIterate(tidstore);
     267        2264 :     while ((iter_result = TidStoreIterateNext(iter)) != NULL)
     268             :     {
     269             :         OffsetNumber offsets[MaxOffsetNumber];
     270             :         int         num_offsets;
     271             : 
     272        2240 :         num_offsets = TidStoreGetBlockOffsets(iter_result, offsets, lengthof(offsets));
     273             :         Assert(num_offsets <= lengthof(offsets));
     274       32760 :         for (int i = 0; i < num_offsets; i++)
     275       30520 :             ItemPointerSet(&(items.iter_tids[num_iter_tids++]), iter_result->blkno,
     276       30520 :                            offsets[i]);
     277             :     }
     278          24 :     TidStoreEndIterate(iter);
     279          24 :     TidStoreUnlock(tidstore);
     280             : 
     281             :     /*
     282             :      * Sort verification and lookup arrays and test that all arrays are the
     283             :      * same.
     284             :      */
     285             : 
     286          24 :     if (num_lookup_tids != items.num_tids)
     287           0 :         elog(ERROR, "should have %d TIDs, have %d", items.num_tids, num_lookup_tids);
     288          24 :     if (num_iter_tids != items.num_tids)
     289           0 :         elog(ERROR, "should have %d TIDs, have %d", items.num_tids, num_iter_tids);
     290             : 
     291          24 :     qsort(items.insert_tids, items.num_tids, sizeof(ItemPointerData), itemptr_cmp);
     292          24 :     qsort(items.lookup_tids, items.num_tids, sizeof(ItemPointerData), itemptr_cmp);
     293       30544 :     for (int i = 0; i < items.num_tids; i++)
     294             :     {
     295       30520 :         if (itemptr_cmp((const void *) &items.insert_tids[i], (const void *) &items.iter_tids[i]) != 0)
     296           0 :             elog(ERROR, "TID iter array doesn't match verification array, got (%u,%u) expected (%u,%u)",
     297             :                  ItemPointerGetBlockNumber(&items.iter_tids[i]),
     298             :                  ItemPointerGetOffsetNumber(&items.iter_tids[i]),
     299             :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
     300             :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
     301       30520 :         if (itemptr_cmp((const void *) &items.insert_tids[i], (const void *) &items.lookup_tids[i]) != 0)
     302           0 :             elog(ERROR, "TID lookup array doesn't match verification array, got (%u,%u) expected (%u,%u)",
     303             :                  ItemPointerGetBlockNumber(&items.lookup_tids[i]),
     304             :                  ItemPointerGetOffsetNumber(&items.lookup_tids[i]),
     305             :                  ItemPointerGetBlockNumber(&items.insert_tids[i]),
     306             :                  ItemPointerGetOffsetNumber(&items.insert_tids[i]));
     307             :     }
     308             : 
     309          24 :     PG_RETURN_VOID();
     310             : }
     311             : 
     312             : /*
     313             :  * In real world use, we care if the memory usage is greater than
     314             :  * some configured limit. Here we just want to verify that
     315             :  * TidStoreMemoryUsage is not broken.
     316             :  */
     317             : Datum
     318           4 : test_is_full(PG_FUNCTION_ARGS)
     319             : {
     320             :     bool        is_full;
     321             : 
     322           4 :     check_tidstore_available();
     323             : 
     324           4 :     is_full = (TidStoreMemoryUsage(tidstore) > tidstore_empty_size);
     325             : 
     326           4 :     PG_RETURN_BOOL(is_full);
     327             : }
     328             : 
     329             : /* Free the tidstore */
     330             : Datum
     331           6 : test_destroy(PG_FUNCTION_ARGS)
     332             : {
     333           6 :     check_tidstore_available();
     334             : 
     335           6 :     TidStoreDestroy(tidstore);
     336           6 :     tidstore = NULL;
     337           6 :     items.num_tids = 0;
     338           6 :     pfree(items.insert_tids);
     339           6 :     pfree(items.lookup_tids);
     340           6 :     pfree(items.iter_tids);
     341             : 
     342           6 :     PG_RETURN_VOID();
     343             : }

Generated by: LCOV version 1.14