Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/backend/tcop/postgres.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ int client_connection_check_interval = 0;

/* flags for non-system relation kinds to restrict use */
int restrict_nonsystem_relation_kind;
#ifdef USE_INJECTION_POINTS
/* Counter which can show us if we are in SMGR */
int inside_smgr_api = 0;
#endif

/* ----------------
* private typedefs etc
Expand Down
26 changes: 21 additions & 5 deletions src/include/miscadmin.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
#include "datatype/timestamp.h" /* for TimestampTz */
#include "pgtime.h" /* for pg_time_t */

#ifdef USE_INJECTION_POINTS
#include "utils/injection_point.h"
#endif


#define InvalidPid (-1)

Expand Down Expand Up @@ -123,18 +127,30 @@ extern process_interrupts_callback_t ProcessInterruptsCallback;
unlikely(InterruptPending))
#endif

/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
#define INTERRUPTS_CAN_BE_PROCESSED() \
(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
QueryCancelHoldoffCount == 0)

#ifdef USE_INJECTION_POINTS
/* Run injection point when inside smgr API; used by CHECK_FOR_INTERRUPTS */
#define CHECK_FOR_INTERRUPTS_SMGR_INJECTION() \
do { \
if (INTERRUPTS_CAN_BE_PROCESSED() && inside_smgr_api > 0) \
INJECTION_POINT("SMGR_API", NULL); \
} while(0)
#else
#define CHECK_FOR_INTERRUPTS_SMGR_INJECTION() ((void)0)
#endif

/* Service interrupt, if one is pending and it's safe to service it now */
#define CHECK_FOR_INTERRUPTS() \
do { \
CHECK_FOR_INTERRUPTS_SMGR_INJECTION(); \
if (INTERRUPTS_PENDING_CONDITION()) \
ProcessInterrupts(); \
} while(0)

/* Is ProcessInterrupts() guaranteed to clear InterruptPending? */
#define INTERRUPTS_CAN_BE_PROCESSED() \
(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
QueryCancelHoldoffCount == 0)

#define HOLD_INTERRUPTS() (InterruptHoldoffCount++)

#define RESUME_INTERRUPTS() \
Expand Down
4 changes: 4 additions & 0 deletions src/include/postgres.h
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,10 @@ Float8GetDatum(float8 X)
extern Datum Float8GetDatum(float8 X);
#endif

/* Declare for neon use */
#ifdef USE_INJECTION_POINTS
extern int inside_smgr_api;
#endif

/*
* Int64GetDatumFast
Expand Down
50 changes: 50 additions & 0 deletions src/test/modules/injection_points/injection_points.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "utils/injection_point.h"
#include "utils/memutils.h"
#include "utils/wait_event.h"
#include <time.h>

PG_MODULE_MAGIC;

Expand Down Expand Up @@ -61,6 +62,8 @@ typedef struct InjectionPointCondition

/* ID of the process where the injection point is allowed to run */
int pid;
/* probability in [0,1], 1.0 = always */
double prob;
} InjectionPointCondition;

/*
Expand Down Expand Up @@ -96,6 +99,8 @@ static InjectionPointSharedState *inj_state = NULL;
extern PGDLLEXPORT void injection_error(const char *name,
const void *private_data,
void *arg);
extern PGDLLEXPORT void injection_error_prob(const char *name,
const void *private_data);
extern PGDLLEXPORT void injection_notice(const char *name,
const void *private_data,
void *arg);
Expand All @@ -105,6 +110,7 @@ extern PGDLLEXPORT void injection_wait(const char *name,

/* track if injection points attached in this process are linked to it */
static bool injection_point_local = false;
static double action2prob(const char *action, int pos);

/*
* GUC variable
Expand Down Expand Up @@ -258,7 +264,20 @@ injection_error(const char *name, const void *private_data, void *arg)
else
elog(ERROR, "error triggered for injection point %s", name);
}
void
injection_error_prob(const char *name, const void *private_data)
{
InjectionPointCondition *condition = (InjectionPointCondition *) private_data;

if (!injection_point_allowed(condition))
return;

/* Use the probability stored in the condition. */
if ((double) rand() / (double) RAND_MAX > condition->prob)
return;

elog(ERROR, "error triggered for injection point %s", name);
}
void
injection_notice(const char *name, const void *private_data, void *arg)
{
Expand Down Expand Up @@ -361,6 +380,11 @@ injection_points_attach(PG_FUNCTION_ARGS)
function = "injection_notice";
else if (strcmp(action, "wait") == 0)
function = "injection_wait";
else if (strncmp(action, "error-prob-", 11) == 0)
{
condition.prob = action2prob(action, 11);
function = "injection_error_prob";
}
else
elog(ERROR, "incorrect action \"%s\" for injection point creation", action);

Expand Down Expand Up @@ -574,3 +598,29 @@ _PG_init(void)
pgstat_register_inj();
pgstat_register_inj_fixed();
}

/*
* Coverts the action name into probability
*/
static double action2prob(const char *action, const int pos)
{
/*
* Simple parser: convert "0-01" -> "0.01" then strtod().
*/
const char *p = action + pos; /* points to "0-01" */
double prob;
char *endptr;
char buf[32];
int i, j;

for (i = 0, j = 0; p[i] != '\0' && j < (int) sizeof(buf) - 1; i++)
{
buf[j++] = (p[i] == '-') ? '.' : p[i];
}
buf[j] = '\0';
errno = 0;
prob = strtod(buf, &endptr);
if (errno != 0 || endptr == buf || prob < 0.0 || prob > 1.0)
elog(ERROR, "invalid probability in action \"%s\"", action);
return prob;
}