Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/test/modules/test_aio/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 18 kB image not shown  

Quelle  test_aio.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * test_aio.c
 *  Helpers to write tests for AIO
 *
 * This module provides interface functions for C functionality to SQL, to
 * make it possible to test AIO related behavior in a targeted way from SQL.
 * It'd not generally be safe to export these functions to SQL, but for a test
 * that's fine.
 *
 * Copyright (c) 2020-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   src/test/modules/test_aio/test_aio.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "access/relation.h"
#include "fmgr.h"
#include "storage/aio.h"
#include "storage/aio_internal.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "storage/checksum.h"
#include "storage/ipc.h"
#include "storage/lwlock.h"
#include "utils/builtins.h"
#include "utils/injection_point.h"
#include "utils/rel.h"


PG_MODULE_MAGIC;


typedef struct InjIoErrorState
{
 bool  enabled_short_read;
 bool  enabled_reopen;

 bool  short_read_result_set;
 int   short_read_result;
} InjIoErrorState;

static InjIoErrorState *inj_io_error_state;

/* Shared memory init callbacks */
static shmem_request_hook_type prev_shmem_request_hook = NULL;
static shmem_startup_hook_type prev_shmem_startup_hook = NULL;


static PgAioHandle *last_handle;



static void
test_aio_shmem_request(void)
{
 if (prev_shmem_request_hook)
  prev_shmem_request_hook();

 RequestAddinShmemSpace(sizeof(InjIoErrorState));
}

static void
test_aio_shmem_startup(void)
{
 bool  found;

 if (prev_shmem_startup_hook)
  prev_shmem_startup_hook();

 /* Create or attach to the shared memory state */
 LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);

 inj_io_error_state = ShmemInitStruct("injection_points",
           sizeof(InjIoErrorState),
           &found);

 if (!found)
 {
  /* First time through, initialize */
  inj_io_error_state->enabled_short_read = false;
  inj_io_error_state->enabled_reopen = false;

#ifdef USE_INJECTION_POINTS
  InjectionPointAttach("aio-process-completion-before-shared",
        "test_aio",
        "inj_io_short_read",
        NULL,
        0);
  InjectionPointLoad("aio-process-completion-before-shared");

  InjectionPointAttach("aio-worker-after-reopen",
        "test_aio",
        "inj_io_reopen",
        NULL,
        0);
  InjectionPointLoad("aio-worker-after-reopen");

#endif
 }
 else
 {
  /*
   * Pre-load the injection points now, so we can call them in a
   * critical section.
 */

#ifdef USE_INJECTION_POINTS
  InjectionPointLoad("aio-process-completion-before-shared");
  InjectionPointLoad("aio-worker-after-reopen");
  elog(LOG, "injection point loaded");
#endif
 }

 LWLockRelease(AddinShmemInitLock);
}

void
_PG_init(void)
{
 if (!process_shared_preload_libraries_in_progress)
  return;

 prev_shmem_request_hook = shmem_request_hook;
 shmem_request_hook = test_aio_shmem_request;
 prev_shmem_startup_hook = shmem_startup_hook;
 shmem_startup_hook = test_aio_shmem_startup;
}


PG_FUNCTION_INFO_V1(errno_from_string);
Datum
errno_from_string(PG_FUNCTION_ARGS)
{
 const char *sym = text_to_cstring(PG_GETARG_TEXT_PP(0));

 if (strcmp(sym, "EIO") == 0)
  PG_RETURN_INT32(EIO);
 else if (strcmp(sym, "EAGAIN") == 0)
  PG_RETURN_INT32(EAGAIN);
 else if (strcmp(sym, "EINTR") == 0)
  PG_RETURN_INT32(EINTR);
 else if (strcmp(sym, "ENOSPC") == 0)
  PG_RETURN_INT32(ENOSPC);
 else if (strcmp(sym, "EROFS") == 0)
  PG_RETURN_INT32(EROFS);

 ereport(ERROR,
   errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   errmsg_internal("%s is not a supported errno value", sym));
 PG_RETURN_INT32(0);
}

PG_FUNCTION_INFO_V1(grow_rel);
Datum
grow_rel(PG_FUNCTION_ARGS)
{
 Oid   relid = PG_GETARG_OID(0);
 uint32  nblocks = PG_GETARG_UINT32(1);
 Relation rel;
#define MAX_BUFFERS_TO_EXTEND_BY 64
 Buffer  victim_buffers[MAX_BUFFERS_TO_EXTEND_BY];

 rel = relation_open(relid, AccessExclusiveLock);

 while (nblocks > 0)
 {
  uint32  extend_by_pages;

  extend_by_pages = Min(nblocks, MAX_BUFFERS_TO_EXTEND_BY);

  ExtendBufferedRelBy(BMR_REL(rel),
       MAIN_FORKNUM,
       NULL,
       0,
       extend_by_pages,
       victim_buffers,
       &extend_by_pages);

  nblocks -= extend_by_pages;

  for (uint32 i = 0; i < extend_by_pages; i++)
  {
   ReleaseBuffer(victim_buffers[i]);
  }
 }

 relation_close(rel, NoLock);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(modify_rel_block);
Datum
modify_rel_block(PG_FUNCTION_ARGS)
{
 Oid   relid = PG_GETARG_OID(0);
 BlockNumber blkno = PG_GETARG_UINT32(1);
 bool  zero = PG_GETARG_BOOL(2);
 bool  corrupt_header = PG_GETARG_BOOL(3);
 bool  corrupt_checksum = PG_GETARG_BOOL(4);
 Page  page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
 bool  flushed;
 Relation rel;
 Buffer  buf;
 PageHeader ph;

 rel = relation_open(relid, AccessExclusiveLock);

 buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
        RBM_ZERO_ON_ERROR, NULL);

 LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);

 /*
  * copy the page to local memory, seems nicer than to directly modify in
  * the buffer pool.
 */

 memcpy(page, BufferGetPage(buf), BLCKSZ);

 LockBuffer(buf, BUFFER_LOCK_UNLOCK);

 ReleaseBuffer(buf);

 /*
  * Don't want to have a buffer in-memory that's marked valid where the
  * on-disk contents are invalid. Particularly not if the in-memory buffer
  * could be dirty...
  *
  * While we hold an AEL on the relation nobody else should be able to read
  * the buffer in.
  *
  * NB: This is probably racy, better don't copy this to non-test code.
 */

 if (BufferIsLocal(buf))
  InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
 else
  EvictUnpinnedBuffer(buf, &flushed);

 /*
  * Now modify the page as asked for by the caller.
 */

 if (zero)
  memset(page, 0, BufferGetPageSize(buf));

 if (PageIsEmpty(page) && (corrupt_header || corrupt_checksum))
  PageInit(page, BufferGetPageSize(buf), 0);

 ph = (PageHeader) page;

 if (corrupt_header)
  ph->pd_special = BLCKSZ + 1;

 if (corrupt_checksum)
 {
  bool  successfully_corrupted = 0;

  /*
   * Any single modification of the checksum could just end up being
   * valid again, due to e.g. corrupt_header changing the data in a way
   * that'd result in the "corrupted" checksum, or the checksum already
   * being invalid. Retry in that, unlikely, case.
 */

  for (int i = 0; i < 100; i++)
  {
   uint16  verify_checksum;
   uint16  old_checksum;

   old_checksum = ph->pd_checksum;
   ph->pd_checksum = old_checksum + 1;

   elog(LOG, "corrupting checksum of blk %u from %u to %u",
     blkno, old_checksum, ph->pd_checksum);

   verify_checksum = pg_checksum_page(page, blkno);
   if (verify_checksum != ph->pd_checksum)
   {
    successfully_corrupted = true;
    break;
   }
  }

  if (!successfully_corrupted)
   elog(ERROR, "could not corrupt checksum, what's going on?");
 }
 else
 {
  PageSetChecksumInplace(page, blkno);
 }

 smgrwrite(RelationGetSmgr(rel),
     MAIN_FORKNUM, blkno, page, true);

 relation_close(rel, NoLock);

 PG_RETURN_VOID();
}

/*
 * Ensures a buffer for rel & blkno is in shared buffers, without actually
 * caring about the buffer contents. Used to set up test scenarios.
 */

static Buffer
create_toy_buffer(Relation rel, BlockNumber blkno)
{
 Buffer  buf;
 BufferDesc *buf_hdr;
 uint32  buf_state;
 bool  was_pinned = false;

 /* place buffer in shared buffers without erroring out */
 buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);
 LockBuffer(buf, BUFFER_LOCK_UNLOCK);

 if (RelationUsesLocalBuffers(rel))
 {
  buf_hdr = GetLocalBufferDescriptor(-buf - 1);
  buf_state = pg_atomic_read_u32(&buf_hdr->state);
 }
 else
 {
  buf_hdr = GetBufferDescriptor(buf - 1);
  buf_state = LockBufHdr(buf_hdr);
 }

 /*
  * We should be the only backend accessing this buffer. This is just a
  * small bit of belt-and-suspenders defense, none of this code should ever
  * run in a cluster with real data.
 */

 if (BUF_STATE_GET_REFCOUNT(buf_state) > 1)
  was_pinned = true;
 else
  buf_state &= ~(BM_VALID | BM_DIRTY);

 if (RelationUsesLocalBuffers(rel))
  pg_atomic_unlocked_write_u32(&buf_hdr->state, buf_state);
 else
  UnlockBufHdr(buf_hdr, buf_state);

 if (was_pinned)
  elog(ERROR, "toy buffer %d was already pinned",
    buf);

 return buf;
}

/*
 * A "low level" read. This does similar things to what
 * StartReadBuffers()/WaitReadBuffers() do, but provides more control (and
 * less sanity).
 */

PG_FUNCTION_INFO_V1(read_rel_block_ll);
Datum
read_rel_block_ll(PG_FUNCTION_ARGS)
{
 Oid   relid = PG_GETARG_OID(0);
 BlockNumber blkno = PG_GETARG_UINT32(1);
 int   nblocks = PG_GETARG_INT32(2);
 bool  wait_complete = PG_GETARG_BOOL(3);
 bool  batchmode_enter = PG_GETARG_BOOL(4);
 bool  call_smgrreleaseall = PG_GETARG_BOOL(5);
 bool  batchmode_exit = PG_GETARG_BOOL(6);
 bool  zero_on_error = PG_GETARG_BOOL(7);
 Relation rel;
 Buffer  bufs[PG_IOV_MAX];
 BufferDesc *buf_hdrs[PG_IOV_MAX];
 Page  pages[PG_IOV_MAX];
 uint8  srb_flags = 0;
 PgAioReturn ior;
 PgAioHandle *ioh;
 PgAioWaitRef iow;
 SMgrRelation smgr;

 if (nblocks <= 0 || nblocks > PG_IOV_MAX)
  elog(ERROR, "nblocks is out of range");

 rel = relation_open(relid, AccessExclusiveLock);

 for (int i = 0; i < nblocks; i++)
 {
  bufs[i] = create_toy_buffer(rel, blkno + i);
  pages[i] = BufferGetBlock(bufs[i]);
  buf_hdrs[i] = BufferIsLocal(bufs[i]) ?
   GetLocalBufferDescriptor(-bufs[i] - 1) :
   GetBufferDescriptor(bufs[i] - 1);
 }

 smgr = RelationGetSmgr(rel);

 pgstat_prepare_report_checksum_failure(smgr->smgr_rlocator.locator.dbOid);

 ioh = pgaio_io_acquire(CurrentResourceOwner, &ior);
 pgaio_io_get_wref(ioh, &iow);

 if (RelationUsesLocalBuffers(rel))
 {
  for (int i = 0; i < nblocks; i++)
   StartLocalBufferIO(buf_hdrs[i], truefalse);
  pgaio_io_set_flag(ioh, PGAIO_HF_REFERENCES_LOCAL);
 }
 else
 {
  for (int i = 0; i < nblocks; i++)
   StartBufferIO(buf_hdrs[i], truefalse);
 }

 pgaio_io_set_handle_data_32(ioh, (uint32 *) bufs, nblocks);

 if (zero_on_error | zero_damaged_pages)
  srb_flags |= READ_BUFFERS_ZERO_ON_ERROR;
 if (ignore_checksum_failure)
  srb_flags |= READ_BUFFERS_IGNORE_CHECKSUM_FAILURES;

 pgaio_io_register_callbacks(ioh,
        RelationUsesLocalBuffers(rel) ?
        PGAIO_HCB_LOCAL_BUFFER_READV :
        PGAIO_HCB_SHARED_BUFFER_READV,
        srb_flags);

 if (batchmode_enter)
  pgaio_enter_batchmode();

 smgrstartreadv(ioh, smgr, MAIN_FORKNUM, blkno,
       (void *) pages, nblocks);

 if (call_smgrreleaseall)
  smgrreleaseall();

 if (batchmode_exit)
  pgaio_exit_batchmode();

 for (int i = 0; i < nblocks; i++)
  ReleaseBuffer(bufs[i]);

 if (wait_complete)
 {
  pgaio_wref_wait(&iow);

  if (ior.result.status != PGAIO_RS_OK)
   pgaio_result_report(ior.result,
        &ior.target_data,
        ior.result.status == PGAIO_RS_ERROR ?
        ERROR : WARNING);
 }

 relation_close(rel, NoLock);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(invalidate_rel_block);
Datum
invalidate_rel_block(PG_FUNCTION_ARGS)
{
 Oid   relid = PG_GETARG_OID(0);
 BlockNumber blkno = PG_GETARG_UINT32(1);
 Relation rel;
 PrefetchBufferResult pr;
 Buffer  buf;

 rel = relation_open(relid, AccessExclusiveLock);

 /*
  * This is a gross hack, but there's no other API exposed that allows to
  * get a buffer ID without actually reading the block in.
 */

 pr = PrefetchBuffer(rel, MAIN_FORKNUM, blkno);
 buf = pr.recent_buffer;

 if (BufferIsValid(buf))
 {
  /* if the buffer contents aren't valid, this'll return false */
  if (ReadRecentBuffer(rel->rd_locator, MAIN_FORKNUM, blkno, buf))
  {
   BufferDesc *buf_hdr = BufferIsLocal(buf) ?
    GetLocalBufferDescriptor(-buf - 1)
    : GetBufferDescriptor(buf - 1);
   bool  flushed;

   LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);

   if (pg_atomic_read_u32(&buf_hdr->state) & BM_DIRTY)
   {
    if (BufferIsLocal(buf))
     FlushLocalBuffer(buf_hdr, NULL);
    else
     FlushOneBuffer(buf);
   }
   LockBuffer(buf, BUFFER_LOCK_UNLOCK);
   ReleaseBuffer(buf);

   if (BufferIsLocal(buf))
    InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
   else if (!EvictUnpinnedBuffer(buf, &flushed))
    elog(ERROR, "couldn't evict");
  }
 }

 relation_close(rel, AccessExclusiveLock);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(buffer_create_toy);
Datum
buffer_create_toy(PG_FUNCTION_ARGS)
{
 Oid   relid = PG_GETARG_OID(0);
 BlockNumber blkno = PG_GETARG_UINT32(1);
 Relation rel;
 Buffer  buf;

 rel = relation_open(relid, AccessExclusiveLock);

 buf = create_toy_buffer(rel, blkno);
 ReleaseBuffer(buf);

 relation_close(rel, NoLock);

 PG_RETURN_INT32(buf);
}

PG_FUNCTION_INFO_V1(buffer_call_start_io);
Datum
buffer_call_start_io(PG_FUNCTION_ARGS)
{
 Buffer  buf = PG_GETARG_INT32(0);
 bool  for_input = PG_GETARG_BOOL(1);
 bool  nowait = PG_GETARG_BOOL(2);
 bool  can_start;

 if (BufferIsLocal(buf))
  can_start = StartLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
            for_input, nowait);
 else
  can_start = StartBufferIO(GetBufferDescriptor(buf - 1),
          for_input, nowait);

 /*
  * For tests we don't want the resowner release preventing us from
  * orchestrating odd scenarios.
 */

 if (can_start && !BufferIsLocal(buf))
  ResourceOwnerForgetBufferIO(CurrentResourceOwner,
         buf);

 ereport(LOG,
   errmsg("buffer %d after StartBufferIO: %s",
       buf, DebugPrintBufferRefcount(buf)),
   errhidestmt(true), errhidecontext(true));

 PG_RETURN_BOOL(can_start);
}

PG_FUNCTION_INFO_V1(buffer_call_terminate_io);
Datum
buffer_call_terminate_io(PG_FUNCTION_ARGS)
{
 Buffer  buf = PG_GETARG_INT32(0);
 bool  for_input = PG_GETARG_BOOL(1);
 bool  succeed = PG_GETARG_BOOL(2);
 bool  io_error = PG_GETARG_BOOL(3);
 bool  release_aio = PG_GETARG_BOOL(4);
 bool  clear_dirty = false;
 uint32  set_flag_bits = 0;

 if (io_error)
  set_flag_bits |= BM_IO_ERROR;

 if (for_input)
 {
  clear_dirty = false;

  if (succeed)
   set_flag_bits |= BM_VALID;
 }
 else
 {
  if (succeed)
   clear_dirty = true;
 }

 ereport(LOG,
   errmsg("buffer %d before Terminate[Local]BufferIO: %s",
       buf, DebugPrintBufferRefcount(buf)),
   errhidestmt(true), errhidecontext(true));

 if (BufferIsLocal(buf))
  TerminateLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
          clear_dirty, set_flag_bits, release_aio);
 else
  TerminateBufferIO(GetBufferDescriptor(buf - 1),
        clear_dirty, set_flag_bits, false, release_aio);

 ereport(LOG,
   errmsg("buffer %d after Terminate[Local]BufferIO: %s",
       buf, DebugPrintBufferRefcount(buf)),
   errhidestmt(true), errhidecontext(true));

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(handle_get);
Datum
handle_get(PG_FUNCTION_ARGS)
{
 last_handle = pgaio_io_acquire(CurrentResourceOwner, NULL);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(handle_release_last);
Datum
handle_release_last(PG_FUNCTION_ARGS)
{
 if (!last_handle)
  elog(ERROR, "no handle");

 pgaio_io_release(last_handle);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(handle_get_and_error);
Datum
handle_get_and_error(PG_FUNCTION_ARGS)
{
 pgaio_io_acquire(CurrentResourceOwner, NULL);

 elog(ERROR, "as you command");
 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(handle_get_twice);
Datum
handle_get_twice(PG_FUNCTION_ARGS)
{
 pgaio_io_acquire(CurrentResourceOwner, NULL);
 pgaio_io_acquire(CurrentResourceOwner, NULL);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(handle_get_release);
Datum
handle_get_release(PG_FUNCTION_ARGS)
{
 PgAioHandle *handle;

 handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
 pgaio_io_release(handle);

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(batch_start);
Datum
batch_start(PG_FUNCTION_ARGS)
{
 pgaio_enter_batchmode();
 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(batch_end);
Datum
batch_end(PG_FUNCTION_ARGS)
{
 pgaio_exit_batchmode();
 PG_RETURN_VOID();
}

#ifdef USE_INJECTION_POINTS
extern PGDLLEXPORT void inj_io_short_read(const char *name,
            const void *private_data,
            void *arg);
extern PGDLLEXPORT void inj_io_reopen(const char *name,
           const void *private_data,
           void *arg);

void
inj_io_short_read(const char *name, const void *private_data, void *arg)
{
 PgAioHandle *ioh = (PgAioHandle *) arg;

 ereport(LOG,
   errmsg("short read injection point called, is enabled: %d",
       inj_io_error_state->enabled_reopen),
   errhidestmt(true), errhidecontext(true));

 if (inj_io_error_state->enabled_short_read)
 {
  /*
   * Only shorten reads that are actually longer than the target size,
   * otherwise we can trigger over-reads.
 */

  if (inj_io_error_state->short_read_result_set
   && ioh->op == PGAIO_OP_READV
   && inj_io_error_state->short_read_result <= ioh->result)
  {
   struct iovec *iov = &pgaio_ctl->iovecs[ioh->iovec_off];
   int32  old_result = ioh->result;
   int32  new_result = inj_io_error_state->short_read_result;
   int32  processed = 0;

   ereport(LOG,
     errmsg("short read inject point, changing result from %d to %d",
         old_result, new_result),
     errhidestmt(true), errhidecontext(true));

   /*
    * The underlying IO actually completed OK, and thus the "invalid"
    * portion of the IOV actually contains valid data. That can hide
    * a lot of problems, e.g. if we were to wrongly mark a buffer,
    * that wasn't read according to the shortened-read, IO as valid,
    * the contents would look valid and we might miss a bug.
    *
    * To avoid that, iterate through the IOV and zero out the
    * "failed" portion of the IO.
 */

   for (int i = 0; i < ioh->op_data.read.iov_length; i++)
   {
    if (processed + iov[i].iov_len <= new_result)
     processed += iov[i].iov_len;
    else if (processed <= new_result)
    {
     uint32  ok_part = new_result - processed;

     memset((char *) iov[i].iov_base + ok_part, 0, iov[i].iov_len - ok_part);
     processed += iov[i].iov_len;
    }
    else
    {
     memset((char *) iov[i].iov_base, 0, iov[i].iov_len);
    }
   }

   ioh->result = new_result;
  }
 }
}

void
inj_io_reopen(const char *name, const void *private_data, void *arg)
{
 ereport(LOG,
   errmsg("reopen injection point called, is enabled: %d",
       inj_io_error_state->enabled_reopen),
   errhidestmt(true), errhidecontext(true));

 if (inj_io_error_state->enabled_reopen)
  elog(ERROR, "injection point triggering failure to reopen ");
}
#endif

PG_FUNCTION_INFO_V1(inj_io_short_read_attach);
Datum
inj_io_short_read_attach(PG_FUNCTION_ARGS)
{
#ifdef USE_INJECTION_POINTS
 inj_io_error_state->enabled_short_read = true;
 inj_io_error_state->short_read_result_set = !PG_ARGISNULL(0);
 if (inj_io_error_state->short_read_result_set)
  inj_io_error_state->short_read_result = PG_GETARG_INT32(0);
#else
 elog(ERROR, "injection points not supported");
#endif

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(inj_io_short_read_detach);
Datum
inj_io_short_read_detach(PG_FUNCTION_ARGS)
{
#ifdef USE_INJECTION_POINTS
 inj_io_error_state->enabled_short_read = false;
#else
 elog(ERROR, "injection points not supported");
#endif
 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(inj_io_reopen_attach);
Datum
inj_io_reopen_attach(PG_FUNCTION_ARGS)
{
#ifdef USE_INJECTION_POINTS
 inj_io_error_state->enabled_reopen = true;
#else
 elog(ERROR, "injection points not supported");
#endif

 PG_RETURN_VOID();
}

PG_FUNCTION_INFO_V1(inj_io_reopen_detach);
Datum
inj_io_reopen_detach(PG_FUNCTION_ARGS)
{
#ifdef USE_INJECTION_POINTS
 inj_io_error_state->enabled_reopen = false;
#else
 elog(ERROR, "injection points not supported");
#endif
 PG_RETURN_VOID();
}

Messung V0.5 in Prozent
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.