LCOV - code coverage report
Current view: top level - src/test/modules/test_aio - test_aio.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 93.4 % 317 296
Test Date: 2026-02-28 15:14:49 Functions: 100.0 % 45 45
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * test_aio.c
       4              :  *      Helpers to write tests for AIO
       5              :  *
       6              :  * This module provides interface functions for C functionality to SQL, to
       7              :  * make it possible to test AIO related behavior in a targeted way from SQL.
       8              :  * It'd not generally be safe to export these functions to SQL, but for a test
       9              :  * that's fine.
      10              :  *
      11              :  * Copyright (c) 2020-2026, PostgreSQL Global Development Group
      12              :  *
      13              :  * IDENTIFICATION
      14              :  *    src/test/modules/test_aio/test_aio.c
      15              :  *
      16              :  *-------------------------------------------------------------------------
      17              :  */
      18              : 
      19              : #include "postgres.h"
      20              : 
      21              : #include "access/relation.h"
      22              : #include "fmgr.h"
      23              : #include "storage/aio.h"
      24              : #include "storage/aio_internal.h"
      25              : #include "storage/buf_internals.h"
      26              : #include "storage/bufmgr.h"
      27              : #include "storage/checksum.h"
      28              : #include "storage/ipc.h"
      29              : #include "storage/lwlock.h"
      30              : #include "utils/builtins.h"
      31              : #include "utils/injection_point.h"
      32              : #include "utils/rel.h"
      33              : 
      34              : 
      35            2 : PG_MODULE_MAGIC;
      36              : 
      37              : 
      38              : typedef struct InjIoErrorState
      39              : {
      40              :     bool        enabled_short_read;
      41              :     bool        enabled_reopen;
      42              : 
      43              :     bool        short_read_result_set;
      44              :     int         short_read_result;
      45              : } InjIoErrorState;
      46              : 
      47              : static InjIoErrorState *inj_io_error_state;
      48              : 
      49              : /* Shared memory init callbacks */
      50              : static shmem_request_hook_type prev_shmem_request_hook = NULL;
      51              : static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
      52              : 
      53              : 
      54              : static PgAioHandle *last_handle;
      55              : 
      56              : 
      57              : 
      58              : static void
      59            2 : test_aio_shmem_request(void)
      60              : {
      61            2 :     if (prev_shmem_request_hook)
      62            0 :         prev_shmem_request_hook();
      63              : 
      64            2 :     RequestAddinShmemSpace(sizeof(InjIoErrorState));
      65            2 : }
      66              : 
      67              : static void
      68            2 : test_aio_shmem_startup(void)
      69              : {
      70              :     bool        found;
      71              : 
      72            2 :     if (prev_shmem_startup_hook)
      73            0 :         prev_shmem_startup_hook();
      74              : 
      75              :     /* Create or attach to the shared memory state */
      76            2 :     LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
      77              : 
      78            2 :     inj_io_error_state = ShmemInitStruct("injection_points",
      79              :                                          sizeof(InjIoErrorState),
      80              :                                          &found);
      81              : 
      82            2 :     if (!found)
      83              :     {
      84              :         /* First time through, initialize */
      85            2 :         inj_io_error_state->enabled_short_read = false;
      86            2 :         inj_io_error_state->enabled_reopen = false;
      87              : 
      88              : #ifdef USE_INJECTION_POINTS
      89            2 :         InjectionPointAttach("aio-process-completion-before-shared",
      90              :                              "test_aio",
      91              :                              "inj_io_short_read",
      92              :                              NULL,
      93              :                              0);
      94            2 :         InjectionPointLoad("aio-process-completion-before-shared");
      95              : 
      96            2 :         InjectionPointAttach("aio-worker-after-reopen",
      97              :                              "test_aio",
      98              :                              "inj_io_reopen",
      99              :                              NULL,
     100              :                              0);
     101            2 :         InjectionPointLoad("aio-worker-after-reopen");
     102              : 
     103              : #endif
     104              :     }
     105              :     else
     106              :     {
     107              :         /*
     108              :          * Pre-load the injection points now, so we can call them in a
     109              :          * critical section.
     110              :          */
     111              : #ifdef USE_INJECTION_POINTS
     112            0 :         InjectionPointLoad("aio-process-completion-before-shared");
     113            0 :         InjectionPointLoad("aio-worker-after-reopen");
     114            0 :         elog(LOG, "injection point loaded");
     115              : #endif
     116              :     }
     117              : 
     118            2 :     LWLockRelease(AddinShmemInitLock);
     119            2 : }
     120              : 
     121              : void
     122            2 : _PG_init(void)
     123              : {
     124            2 :     if (!process_shared_preload_libraries_in_progress)
     125            0 :         return;
     126              : 
     127            2 :     prev_shmem_request_hook = shmem_request_hook;
     128            2 :     shmem_request_hook = test_aio_shmem_request;
     129            2 :     prev_shmem_startup_hook = shmem_startup_hook;
     130            2 :     shmem_startup_hook = test_aio_shmem_startup;
     131              : }
     132              : 
     133              : 
     134            6 : PG_FUNCTION_INFO_V1(errno_from_string);
     135              : Datum
     136            4 : errno_from_string(PG_FUNCTION_ARGS)
     137              : {
     138            4 :     const char *sym = text_to_cstring(PG_GETARG_TEXT_PP(0));
     139              : 
     140            4 :     if (strcmp(sym, "EIO") == 0)
     141            2 :         PG_RETURN_INT32(EIO);
     142            2 :     else if (strcmp(sym, "EAGAIN") == 0)
     143            0 :         PG_RETURN_INT32(EAGAIN);
     144            2 :     else if (strcmp(sym, "EINTR") == 0)
     145            0 :         PG_RETURN_INT32(EINTR);
     146            2 :     else if (strcmp(sym, "ENOSPC") == 0)
     147            0 :         PG_RETURN_INT32(ENOSPC);
     148            2 :     else if (strcmp(sym, "EROFS") == 0)
     149            2 :         PG_RETURN_INT32(EROFS);
     150              : 
     151            0 :     ereport(ERROR,
     152              :             errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     153              :             errmsg_internal("%s is not a supported errno value", sym));
     154              :     PG_RETURN_INT32(0);
     155              : }
     156              : 
     157            8 : PG_FUNCTION_INFO_V1(grow_rel);
     158              : Datum
     159            6 : grow_rel(PG_FUNCTION_ARGS)
     160              : {
     161            6 :     Oid         relid = PG_GETARG_OID(0);
     162            6 :     uint32      nblocks = PG_GETARG_UINT32(1);
     163              :     Relation    rel;
     164              : #define MAX_BUFFERS_TO_EXTEND_BY 64
     165              :     Buffer      victim_buffers[MAX_BUFFERS_TO_EXTEND_BY];
     166              : 
     167            6 :     rel = relation_open(relid, AccessExclusiveLock);
     168              : 
     169           12 :     while (nblocks > 0)
     170              :     {
     171              :         uint32      extend_by_pages;
     172              : 
     173            6 :         extend_by_pages = Min(nblocks, MAX_BUFFERS_TO_EXTEND_BY);
     174              : 
     175            6 :         ExtendBufferedRelBy(BMR_REL(rel),
     176              :                             MAIN_FORKNUM,
     177              :                             NULL,
     178              :                             0,
     179              :                             extend_by_pages,
     180              :                             victim_buffers,
     181              :                             &extend_by_pages);
     182              : 
     183            6 :         nblocks -= extend_by_pages;
     184              : 
     185           78 :         for (uint32 i = 0; i < extend_by_pages; i++)
     186              :         {
     187           72 :             ReleaseBuffer(victim_buffers[i]);
     188              :         }
     189              :     }
     190              : 
     191            6 :     relation_close(rel, NoLock);
     192              : 
     193            6 :     PG_RETURN_VOID();
     194              : }
     195              : 
     196           18 : PG_FUNCTION_INFO_V1(modify_rel_block);
     197              : Datum
     198           70 : modify_rel_block(PG_FUNCTION_ARGS)
     199              : {
     200           70 :     Oid         relid = PG_GETARG_OID(0);
     201           70 :     BlockNumber blkno = PG_GETARG_UINT32(1);
     202           70 :     bool        zero = PG_GETARG_BOOL(2);
     203           70 :     bool        corrupt_header = PG_GETARG_BOOL(3);
     204           70 :     bool        corrupt_checksum = PG_GETARG_BOOL(4);
     205           70 :     Page        page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
     206              :     bool        flushed;
     207              :     Relation    rel;
     208              :     Buffer      buf;
     209              :     PageHeader  ph;
     210              : 
     211           70 :     rel = relation_open(relid, AccessExclusiveLock);
     212              : 
     213           70 :     buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
     214              :                              RBM_ZERO_ON_ERROR, NULL);
     215              : 
     216           70 :     LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     217              : 
     218              :     /*
     219              :      * copy the page to local memory, seems nicer than to directly modify in
     220              :      * the buffer pool.
     221              :      */
     222           70 :     memcpy(page, BufferGetPage(buf), BLCKSZ);
     223              : 
     224           70 :     LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     225              : 
     226           70 :     ReleaseBuffer(buf);
     227              : 
     228              :     /*
     229              :      * Don't want to have a buffer in-memory that's marked valid where the
     230              :      * on-disk contents are invalid. Particularly not if the in-memory buffer
     231              :      * could be dirty...
     232              :      *
     233              :      * While we hold an AEL on the relation nobody else should be able to read
     234              :      * the buffer in.
     235              :      *
     236              :      * NB: This is probably racy, better don't copy this to non-test code.
     237              :      */
     238           70 :     if (BufferIsLocal(buf))
     239           18 :         InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
     240              :     else
     241           52 :         EvictUnpinnedBuffer(buf, &flushed);
     242              : 
     243              :     /*
     244              :      * Now modify the page as asked for by the caller.
     245              :      */
     246           70 :     if (zero)
     247           16 :         memset(page, 0, BufferGetPageSize(buf));
     248              : 
     249           70 :     if (PageIsEmpty(page) && (corrupt_header || corrupt_checksum))
     250           16 :         PageInit(page, BufferGetPageSize(buf), 0);
     251              : 
     252           70 :     ph = (PageHeader) page;
     253              : 
     254           70 :     if (corrupt_header)
     255           32 :         ph->pd_special = BLCKSZ + 1;
     256              : 
     257           70 :     if (corrupt_checksum)
     258              :     {
     259           26 :         bool        successfully_corrupted = 0;
     260              : 
     261              :         /*
     262              :          * Any single modification of the checksum could just end up being
     263              :          * valid again, due to e.g. corrupt_header changing the data in a way
     264              :          * that'd result in the "corrupted" checksum, or the checksum already
     265              :          * being invalid. Retry in that, unlikely, case.
     266              :          */
     267           26 :         for (int i = 0; i < 100; i++)
     268              :         {
     269              :             uint16      verify_checksum;
     270              :             uint16      old_checksum;
     271              : 
     272           26 :             old_checksum = ph->pd_checksum;
     273           26 :             ph->pd_checksum = old_checksum + 1;
     274              : 
     275           26 :             elog(LOG, "corrupting checksum of blk %u from %u to %u",
     276              :                  blkno, old_checksum, ph->pd_checksum);
     277              : 
     278           26 :             verify_checksum = pg_checksum_page(page, blkno);
     279           26 :             if (verify_checksum != ph->pd_checksum)
     280              :             {
     281           26 :                 successfully_corrupted = true;
     282           26 :                 break;
     283              :             }
     284              :         }
     285              : 
     286           26 :         if (!successfully_corrupted)
     287            0 :             elog(ERROR, "could not corrupt checksum, what's going on?");
     288              :     }
     289              :     else
     290              :     {
     291           44 :         PageSetChecksumInplace(page, blkno);
     292              :     }
     293              : 
     294           70 :     smgrwrite(RelationGetSmgr(rel),
     295              :               MAIN_FORKNUM, blkno, page, true);
     296              : 
     297           70 :     relation_close(rel, NoLock);
     298              : 
     299           70 :     PG_RETURN_VOID();
     300              : }
     301              : 
     302              : /*
     303              :  * Ensures a buffer for rel & blkno is in shared buffers, without actually
     304              :  * caring about the buffer contents. Used to set up test scenarios.
     305              :  */
     306              : static Buffer
     307          130 : create_toy_buffer(Relation rel, BlockNumber blkno)
     308              : {
     309              :     Buffer      buf;
     310              :     BufferDesc *buf_hdr;
     311              :     uint64      buf_state;
     312          130 :     bool        was_pinned = false;
     313          130 :     uint64      unset_bits = 0;
     314              : 
     315              :     /* place buffer in shared buffers without erroring out */
     316          130 :     buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);
     317          130 :     LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     318              : 
     319          130 :     if (RelationUsesLocalBuffers(rel))
     320              :     {
     321           44 :         buf_hdr = GetLocalBufferDescriptor(-buf - 1);
     322           44 :         buf_state = pg_atomic_read_u64(&buf_hdr->state);
     323              :     }
     324              :     else
     325              :     {
     326           86 :         buf_hdr = GetBufferDescriptor(buf - 1);
     327           86 :         buf_state = LockBufHdr(buf_hdr);
     328              :     }
     329              : 
     330              :     /*
     331              :      * We should be the only backend accessing this buffer. This is just a
     332              :      * small bit of belt-and-suspenders defense, none of this code should ever
     333              :      * run in a cluster with real data.
     334              :      */
     335          130 :     if (BUF_STATE_GET_REFCOUNT(buf_state) > 1)
     336            0 :         was_pinned = true;
     337              :     else
     338          130 :         unset_bits |= BM_VALID | BM_DIRTY;
     339              : 
     340          130 :     if (RelationUsesLocalBuffers(rel))
     341              :     {
     342           44 :         buf_state &= ~unset_bits;
     343           44 :         pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
     344              :     }
     345              :     else
     346              :     {
     347           86 :         UnlockBufHdrExt(buf_hdr, buf_state, 0, unset_bits, 0);
     348              :     }
     349              : 
     350          130 :     if (was_pinned)
     351            0 :         elog(ERROR, "toy buffer %d was already pinned",
     352              :              buf);
     353              : 
     354          130 :     return buf;
     355              : }
     356              : 
     357              : /*
     358              :  * A "low level" read. This does similar things to what
     359              :  * StartReadBuffers()/WaitReadBuffers() do, but provides more control (and
     360              :  * less sanity).
     361              :  */
     362           20 : PG_FUNCTION_INFO_V1(read_rel_block_ll);
     363              : Datum
     364           72 : read_rel_block_ll(PG_FUNCTION_ARGS)
     365              : {
     366           72 :     Oid         relid = PG_GETARG_OID(0);
     367           72 :     BlockNumber blkno = PG_GETARG_UINT32(1);
     368           72 :     int         nblocks = PG_GETARG_INT32(2);
     369           72 :     bool        wait_complete = PG_GETARG_BOOL(3);
     370           72 :     bool        batchmode_enter = PG_GETARG_BOOL(4);
     371           72 :     bool        call_smgrreleaseall = PG_GETARG_BOOL(5);
     372           72 :     bool        batchmode_exit = PG_GETARG_BOOL(6);
     373           72 :     bool        zero_on_error = PG_GETARG_BOOL(7);
     374              :     Relation    rel;
     375              :     Buffer      bufs[PG_IOV_MAX];
     376              :     BufferDesc *buf_hdrs[PG_IOV_MAX];
     377              :     Page        pages[PG_IOV_MAX];
     378           72 :     uint8       srb_flags = 0;
     379              :     PgAioReturn ior;
     380              :     PgAioHandle *ioh;
     381              :     PgAioWaitRef iow;
     382              :     SMgrRelation smgr;
     383              : 
     384           72 :     if (nblocks <= 0 || nblocks > PG_IOV_MAX)
     385            0 :         elog(ERROR, "nblocks is out of range");
     386              : 
     387           72 :     rel = relation_open(relid, AccessExclusiveLock);
     388              : 
     389          196 :     for (int i = 0; i < nblocks; i++)
     390              :     {
     391          124 :         bufs[i] = create_toy_buffer(rel, blkno + i);
     392          124 :         pages[i] = BufferGetBlock(bufs[i]);
     393          124 :         buf_hdrs[i] = BufferIsLocal(bufs[i]) ?
     394          124 :             GetLocalBufferDescriptor(-bufs[i] - 1) :
     395           82 :             GetBufferDescriptor(bufs[i] - 1);
     396              :     }
     397              : 
     398           72 :     smgr = RelationGetSmgr(rel);
     399              : 
     400           72 :     pgstat_prepare_report_checksum_failure(smgr->smgr_rlocator.locator.dbOid);
     401              : 
     402           72 :     ioh = pgaio_io_acquire(CurrentResourceOwner, &ior);
     403           72 :     pgaio_io_get_wref(ioh, &iow);
     404              : 
     405           72 :     if (RelationUsesLocalBuffers(rel))
     406              :     {
     407           64 :         for (int i = 0; i < nblocks; i++)
     408           42 :             StartLocalBufferIO(buf_hdrs[i], true, false);
     409           22 :         pgaio_io_set_flag(ioh, PGAIO_HF_REFERENCES_LOCAL);
     410              :     }
     411              :     else
     412              :     {
     413          132 :         for (int i = 0; i < nblocks; i++)
     414           82 :             StartBufferIO(buf_hdrs[i], true, false);
     415              :     }
     416              : 
     417           72 :     pgaio_io_set_handle_data_32(ioh, (uint32 *) bufs, nblocks);
     418              : 
     419           72 :     if (zero_on_error | zero_damaged_pages)
     420           22 :         srb_flags |= READ_BUFFERS_ZERO_ON_ERROR;
     421           72 :     if (ignore_checksum_failure)
     422           10 :         srb_flags |= READ_BUFFERS_IGNORE_CHECKSUM_FAILURES;
     423              : 
     424           72 :     pgaio_io_register_callbacks(ioh,
     425           72 :                                 RelationUsesLocalBuffers(rel) ?
     426              :                                 PGAIO_HCB_LOCAL_BUFFER_READV :
     427              :                                 PGAIO_HCB_SHARED_BUFFER_READV,
     428              :                                 srb_flags);
     429              : 
     430           72 :     if (batchmode_enter)
     431            4 :         pgaio_enter_batchmode();
     432              : 
     433           72 :     smgrstartreadv(ioh, smgr, MAIN_FORKNUM, blkno,
     434              :                    (void *) pages, nblocks);
     435              : 
     436           72 :     if (call_smgrreleaseall)
     437            4 :         smgrreleaseall();
     438              : 
     439           72 :     if (batchmode_exit)
     440            4 :         pgaio_exit_batchmode();
     441              : 
     442          196 :     for (int i = 0; i < nblocks; i++)
     443          124 :         ReleaseBuffer(bufs[i]);
     444              : 
     445           72 :     if (wait_complete)
     446              :     {
     447           50 :         pgaio_wref_wait(&iow);
     448              : 
     449           50 :         if (ior.result.status != PGAIO_RS_OK)
     450           44 :             pgaio_result_report(ior.result,
     451              :                                 &ior.target_data,
     452           44 :                                 ior.result.status == PGAIO_RS_ERROR ?
     453              :                                 ERROR : WARNING);
     454              :     }
     455              : 
     456           50 :     relation_close(rel, NoLock);
     457              : 
     458           50 :     PG_RETURN_VOID();
     459              : }
     460              : 
     461           11 : PG_FUNCTION_INFO_V1(invalidate_rel_block);
     462              : Datum
     463          139 : invalidate_rel_block(PG_FUNCTION_ARGS)
     464              : {
     465          139 :     Oid         relid = PG_GETARG_OID(0);
     466          139 :     BlockNumber blkno = PG_GETARG_UINT32(1);
     467              :     Relation    rel;
     468              :     PrefetchBufferResult pr;
     469              :     Buffer      buf;
     470              : 
     471          139 :     rel = relation_open(relid, AccessExclusiveLock);
     472              : 
     473              :     /*
     474              :      * This is a gross hack, but there's no other API exposed that allows to
     475              :      * get a buffer ID without actually reading the block in.
     476              :      */
     477          139 :     pr = PrefetchBuffer(rel, MAIN_FORKNUM, blkno);
     478          139 :     buf = pr.recent_buffer;
     479              : 
     480          139 :     if (BufferIsValid(buf))
     481              :     {
     482              :         /* if the buffer contents aren't valid, this'll return false */
     483          125 :         if (ReadRecentBuffer(rel->rd_locator, MAIN_FORKNUM, blkno, buf))
     484              :         {
     485          119 :             BufferDesc *buf_hdr = BufferIsLocal(buf) ?
     486           32 :                 GetLocalBufferDescriptor(-buf - 1)
     487          119 :                 : GetBufferDescriptor(buf - 1);
     488              :             bool        flushed;
     489              : 
     490          119 :             LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     491              : 
     492          119 :             if (pg_atomic_read_u64(&buf_hdr->state) & BM_DIRTY)
     493              :             {
     494           74 :                 if (BufferIsLocal(buf))
     495           22 :                     FlushLocalBuffer(buf_hdr, NULL);
     496              :                 else
     497           52 :                     FlushOneBuffer(buf);
     498              :             }
     499          119 :             LockBuffer(buf, BUFFER_LOCK_UNLOCK);
     500          119 :             ReleaseBuffer(buf);
     501              : 
     502          119 :             if (BufferIsLocal(buf))
     503           32 :                 InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
     504           87 :             else if (!EvictUnpinnedBuffer(buf, &flushed))
     505            0 :                 elog(ERROR, "couldn't evict");
     506              :         }
     507              :     }
     508              : 
     509          139 :     relation_close(rel, AccessExclusiveLock);
     510              : 
     511          139 :     PG_RETURN_VOID();
     512              : }
     513              : 
     514            6 : PG_FUNCTION_INFO_V1(buffer_create_toy);
     515              : Datum
     516            6 : buffer_create_toy(PG_FUNCTION_ARGS)
     517              : {
     518            6 :     Oid         relid = PG_GETARG_OID(0);
     519            6 :     BlockNumber blkno = PG_GETARG_UINT32(1);
     520              :     Relation    rel;
     521              :     Buffer      buf;
     522              : 
     523            6 :     rel = relation_open(relid, AccessExclusiveLock);
     524              : 
     525            6 :     buf = create_toy_buffer(rel, blkno);
     526            6 :     ReleaseBuffer(buf);
     527              : 
     528            6 :     relation_close(rel, NoLock);
     529              : 
     530            6 :     PG_RETURN_INT32(buf);
     531              : }
     532              : 
     533            8 : PG_FUNCTION_INFO_V1(buffer_call_start_io);
     534              : Datum
     535           20 : buffer_call_start_io(PG_FUNCTION_ARGS)
     536              : {
     537           20 :     Buffer      buf = PG_GETARG_INT32(0);
     538           20 :     bool        for_input = PG_GETARG_BOOL(1);
     539           20 :     bool        nowait = PG_GETARG_BOOL(2);
     540              :     bool        can_start;
     541              : 
     542           20 :     if (BufferIsLocal(buf))
     543            8 :         can_start = StartLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
     544              :                                        for_input, nowait);
     545              :     else
     546           12 :         can_start = StartBufferIO(GetBufferDescriptor(buf - 1),
     547              :                                   for_input, nowait);
     548              : 
     549              :     /*
     550              :      * For tests we don't want the resowner release preventing us from
     551              :      * orchestrating odd scenarios.
     552              :      */
     553           20 :     if (can_start && !BufferIsLocal(buf))
     554            6 :         ResourceOwnerForgetBufferIO(CurrentResourceOwner,
     555              :                                     buf);
     556              : 
     557           20 :     ereport(LOG,
     558              :             errmsg("buffer %d after StartBufferIO: %s",
     559              :                    buf, DebugPrintBufferRefcount(buf)),
     560              :             errhidestmt(true), errhidecontext(true));
     561              : 
     562           20 :     PG_RETURN_BOOL(can_start);
     563              : }
     564              : 
     565            8 : PG_FUNCTION_INFO_V1(buffer_call_terminate_io);
     566              : Datum
     567           10 : buffer_call_terminate_io(PG_FUNCTION_ARGS)
     568              : {
     569           10 :     Buffer      buf = PG_GETARG_INT32(0);
     570           10 :     bool        for_input = PG_GETARG_BOOL(1);
     571           10 :     bool        succeed = PG_GETARG_BOOL(2);
     572           10 :     bool        io_error = PG_GETARG_BOOL(3);
     573           10 :     bool        release_aio = PG_GETARG_BOOL(4);
     574           10 :     bool        clear_dirty = false;
     575           10 :     uint64      set_flag_bits = 0;
     576              : 
     577           10 :     if (io_error)
     578            0 :         set_flag_bits |= BM_IO_ERROR;
     579              : 
     580           10 :     if (for_input)
     581              :     {
     582           10 :         clear_dirty = false;
     583              : 
     584           10 :         if (succeed)
     585            4 :             set_flag_bits |= BM_VALID;
     586              :     }
     587              :     else
     588              :     {
     589            0 :         if (succeed)
     590            0 :             clear_dirty = true;
     591              :     }
     592              : 
     593           10 :     ereport(LOG,
     594              :             errmsg("buffer %d before Terminate[Local]BufferIO: %s",
     595              :                    buf, DebugPrintBufferRefcount(buf)),
     596              :             errhidestmt(true), errhidecontext(true));
     597              : 
     598           10 :     if (BufferIsLocal(buf))
     599            4 :         TerminateLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
     600              :                                clear_dirty, set_flag_bits, release_aio);
     601              :     else
     602            6 :         TerminateBufferIO(GetBufferDescriptor(buf - 1),
     603              :                           clear_dirty, set_flag_bits, false, release_aio);
     604              : 
     605           10 :     ereport(LOG,
     606              :             errmsg("buffer %d after Terminate[Local]BufferIO: %s",
     607              :                    buf, DebugPrintBufferRefcount(buf)),
     608              :             errhidestmt(true), errhidecontext(true));
     609              : 
     610           10 :     PG_RETURN_VOID();
     611              : }
     612              : 
     613            6 : PG_FUNCTION_INFO_V1(handle_get);
     614              : Datum
     615           12 : handle_get(PG_FUNCTION_ARGS)
     616              : {
     617           12 :     last_handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
     618              : 
     619           12 :     PG_RETURN_VOID();
     620              : }
     621              : 
     622            6 : PG_FUNCTION_INFO_V1(handle_release_last);
     623              : Datum
     624            4 : handle_release_last(PG_FUNCTION_ARGS)
     625              : {
     626            4 :     if (!last_handle)
     627            0 :         elog(ERROR, "no handle");
     628              : 
     629            4 :     pgaio_io_release(last_handle);
     630              : 
     631            2 :     PG_RETURN_VOID();
     632              : }
     633              : 
     634            6 : PG_FUNCTION_INFO_V1(handle_get_and_error);
     635              : Datum
     636            6 : handle_get_and_error(PG_FUNCTION_ARGS)
     637              : {
     638            6 :     pgaio_io_acquire(CurrentResourceOwner, NULL);
     639              : 
     640            6 :     elog(ERROR, "as you command");
     641              :     PG_RETURN_VOID();
     642              : }
     643              : 
     644            6 : PG_FUNCTION_INFO_V1(handle_get_twice);
     645              : Datum
     646            2 : handle_get_twice(PG_FUNCTION_ARGS)
     647              : {
     648            2 :     pgaio_io_acquire(CurrentResourceOwner, NULL);
     649            2 :     pgaio_io_acquire(CurrentResourceOwner, NULL);
     650              : 
     651            0 :     PG_RETURN_VOID();
     652              : }
     653              : 
     654            6 : PG_FUNCTION_INFO_V1(handle_get_release);
     655              : Datum
     656            6 : handle_get_release(PG_FUNCTION_ARGS)
     657              : {
     658              :     PgAioHandle *handle;
     659              : 
     660            6 :     handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
     661            6 :     pgaio_io_release(handle);
     662              : 
     663            6 :     PG_RETURN_VOID();
     664              : }
     665              : 
     666            6 : PG_FUNCTION_INFO_V1(batch_start);
     667              : Datum
     668            6 : batch_start(PG_FUNCTION_ARGS)
     669              : {
     670            6 :     pgaio_enter_batchmode();
     671            6 :     PG_RETURN_VOID();
     672              : }
     673              : 
     674            6 : PG_FUNCTION_INFO_V1(batch_end);
     675              : Datum
     676            2 : batch_end(PG_FUNCTION_ARGS)
     677              : {
     678            2 :     pgaio_exit_batchmode();
     679            2 :     PG_RETURN_VOID();
     680              : }
     681              : 
     682              : #ifdef USE_INJECTION_POINTS
     683              : extern PGDLLEXPORT void inj_io_short_read(const char *name,
     684              :                                           const void *private_data,
     685              :                                           void *arg);
     686              : extern PGDLLEXPORT void inj_io_reopen(const char *name,
     687              :                                       const void *private_data,
     688              :                                       void *arg);
     689              : 
     690              : void
     691         1269 : inj_io_short_read(const char *name, const void *private_data, void *arg)
     692              : {
     693         1269 :     PgAioHandle *ioh = (PgAioHandle *) arg;
     694              : 
     695         1269 :     ereport(LOG,
     696              :             errmsg("short read injection point called, is enabled: %d",
     697              :                    inj_io_error_state->enabled_reopen),
     698              :             errhidestmt(true), errhidecontext(true));
     699              : 
     700         1269 :     if (inj_io_error_state->enabled_short_read)
     701              :     {
     702              :         /*
     703              :          * Only shorten reads that are actually longer than the target size,
     704              :          * otherwise we can trigger over-reads.
     705              :          */
     706           48 :         if (inj_io_error_state->short_read_result_set
     707           48 :             && ioh->op == PGAIO_OP_READV
     708           48 :             && inj_io_error_state->short_read_result <= ioh->result)
     709              :         {
     710           44 :             struct iovec *iov = &pgaio_ctl->iovecs[ioh->iovec_off];
     711           44 :             int32       old_result = ioh->result;
     712           44 :             int32       new_result = inj_io_error_state->short_read_result;
     713           44 :             int32       processed = 0;
     714              : 
     715           44 :             ereport(LOG,
     716              :                     errmsg("short read inject point, changing result from %d to %d",
     717              :                            old_result, new_result),
     718              :                     errhidestmt(true), errhidecontext(true));
     719              : 
     720              :             /*
     721              :              * The underlying IO actually completed OK, and thus the "invalid"
     722              :              * portion of the IOV actually contains valid data. That can hide
     723              :              * a lot of problems, e.g. if we were to wrongly mark a buffer,
     724              :              * that wasn't read according to the shortened-read, IO as valid,
     725              :              * the contents would look valid and we might miss a bug.
     726              :              *
     727              :              * To avoid that, iterate through the IOV and zero out the
     728              :              * "failed" portion of the IO.
     729              :              */
     730           92 :             for (int i = 0; i < ioh->op_data.read.iov_length; i++)
     731              :             {
     732           48 :                 if (processed + iov[i].iov_len <= new_result)
     733           34 :                     processed += iov[i].iov_len;
     734           14 :                 else if (processed <= new_result)
     735              :                 {
     736           14 :                     uint32      ok_part = new_result - processed;
     737              : 
     738           14 :                     memset((char *) iov[i].iov_base + ok_part, 0, iov[i].iov_len - ok_part);
     739           14 :                     processed += iov[i].iov_len;
     740              :                 }
     741              :                 else
     742              :                 {
     743            0 :                     memset((char *) iov[i].iov_base, 0, iov[i].iov_len);
     744              :                 }
     745              :             }
     746              : 
     747           44 :             ioh->result = new_result;
     748              :         }
     749              :     }
     750         1269 : }
     751              : 
     752              : void
     753          346 : inj_io_reopen(const char *name, const void *private_data, void *arg)
     754              : {
     755          346 :     ereport(LOG,
     756              :             errmsg("reopen injection point called, is enabled: %d",
     757              :                    inj_io_error_state->enabled_reopen),
     758              :             errhidestmt(true), errhidecontext(true));
     759              : 
     760          346 :     if (inj_io_error_state->enabled_reopen)
     761            1 :         elog(ERROR, "injection point triggering failure to reopen ");
     762          345 : }
     763              : #endif
     764              : 
     765            6 : PG_FUNCTION_INFO_V1(inj_io_short_read_attach);
     766              : Datum
     767           14 : inj_io_short_read_attach(PG_FUNCTION_ARGS)
     768              : {
     769              : #ifdef USE_INJECTION_POINTS
     770           14 :     inj_io_error_state->enabled_short_read = true;
     771           14 :     inj_io_error_state->short_read_result_set = !PG_ARGISNULL(0);
     772           14 :     if (inj_io_error_state->short_read_result_set)
     773           14 :         inj_io_error_state->short_read_result = PG_GETARG_INT32(0);
     774              : #else
     775              :     elog(ERROR, "injection points not supported");
     776              : #endif
     777              : 
     778           14 :     PG_RETURN_VOID();
     779              : }
     780              : 
     781            6 : PG_FUNCTION_INFO_V1(inj_io_short_read_detach);
     782              : Datum
     783            4 : inj_io_short_read_detach(PG_FUNCTION_ARGS)
     784              : {
     785              : #ifdef USE_INJECTION_POINTS
     786            4 :     inj_io_error_state->enabled_short_read = false;
     787              : #else
     788              :     elog(ERROR, "injection points not supported");
     789              : #endif
     790            4 :     PG_RETURN_VOID();
     791              : }
     792              : 
     793            5 : PG_FUNCTION_INFO_V1(inj_io_reopen_attach);
     794              : Datum
     795            1 : inj_io_reopen_attach(PG_FUNCTION_ARGS)
     796              : {
     797              : #ifdef USE_INJECTION_POINTS
     798            1 :     inj_io_error_state->enabled_reopen = true;
     799              : #else
     800              :     elog(ERROR, "injection points not supported");
     801              : #endif
     802              : 
     803            1 :     PG_RETURN_VOID();
     804              : }
     805              : 
     806            5 : PG_FUNCTION_INFO_V1(inj_io_reopen_detach);
     807              : Datum
     808            1 : inj_io_reopen_detach(PG_FUNCTION_ARGS)
     809              : {
     810              : #ifdef USE_INJECTION_POINTS
     811            1 :     inj_io_error_state->enabled_reopen = false;
     812              : #else
     813              :     elog(ERROR, "injection points not supported");
     814              : #endif
     815            1 :     PG_RETURN_VOID();
     816              : }
        

Generated by: LCOV version 2.0-1