From e5dc9d71be408d4dc801815550ffdf76b5046141 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Thu, 25 Dec 2025 14:24:02 +0100 Subject: [PATCH 01/10] Add the injection point got SMGR --- src/backend/tcop/postgres.c | 6 ++++++ .../injection_points/injection_points.c | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 5ff0d0bd177..bb7b92fafa3 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3275,6 +3275,12 @@ ProcessInterrupts(void) /* OK to accept any interrupts now? */ if (InterruptHoldoffCount != 0 || CritSectionCount != 0) return; +#ifdef INJECTION_POINTS + if (inside_smgr_api > 0) + { + INJECTION_POINT("SMGR_API"); + } +#endif InterruptPending = false; retry: diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 1b695a18203..838bd751936 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -16,6 +16,7 @@ */ #include "postgres.h" +#include #include "fmgr.h" #include "miscadmin.h" @@ -88,6 +89,8 @@ static InjectionPointSharedState *inj_state = NULL; extern PGDLLEXPORT void injection_error(const char *name, const void *private_data); +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); extern PGDLLEXPORT void injection_wait(const char *name, @@ -184,7 +187,20 @@ injection_error(const char *name, const void *private_data) 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; + + srand((unsigned int)time(NULL)); + if ( rand() % 10000 > 0) + return; + elog(ERROR, "error triggered for injection point %s", name); +} void injection_notice(const char *name, const void *private_data) { @@ -278,6 +294,8 @@ injection_points_attach(PG_FUNCTION_ARGS) function = "injection_notice"; else if (strcmp(action, "wait") == 0) function = "injection_wait"; + else if (strcmp(action, "error-prob") == 0) + function = "injection_error_prob"; else elog(ERROR, "incorrect action \"%s\" for injection point creation", action); From 40201b9edeb81d16d5ce22d4ffd69a1ac9d2ab06 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Thu, 15 Jan 2026 19:15:07 +0100 Subject: [PATCH 02/10] Fix problems --- src/backend/tcop/postgres.c | 2 +- src/test/modules/injection_points/injection_points.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index bb7b92fafa3..e1e3b5d3d97 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3275,7 +3275,7 @@ ProcessInterrupts(void) /* OK to accept any interrupts now? */ if (InterruptHoldoffCount != 0 || CritSectionCount != 0) return; -#ifdef INJECTION_POINTS +#ifdef USE_INJECTION_POINTS if (inside_smgr_api > 0) { INJECTION_POINT("SMGR_API"); diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 838bd751936..f985d9dc415 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -89,7 +89,7 @@ static InjectionPointSharedState *inj_state = NULL; extern PGDLLEXPORT void injection_error(const char *name, const void *private_data); -extern PGDLLEXPORT void injection_error_prob(const char *name, +extern PGDLLEXPORT void injection_error_prob_0_01(const char *name, const void *private_data); extern PGDLLEXPORT void injection_notice(const char *name, const void *private_data); @@ -188,7 +188,7 @@ injection_error(const char *name, const void *private_data) elog(ERROR, "error triggered for injection point %s", name); } void -injection_error_prob(const char *name, const void *private_data) +injection_error_prob_0_01(const char *name, const void *private_data) { InjectionPointCondition *condition = (InjectionPointCondition *) private_data; @@ -294,8 +294,8 @@ injection_points_attach(PG_FUNCTION_ARGS) function = "injection_notice"; else if (strcmp(action, "wait") == 0) function = "injection_wait"; - else if (strcmp(action, "error-prob") == 0) - function = "injection_error_prob"; + else if (strcmp(action, "error-prob-0-01") == 0) + function = "injection_error_prob_0_01"; else elog(ERROR, "incorrect action \"%s\" for injection point creation", action); From b67c05926c85d1dd1c13b4ae8366b077c2a40ecb Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Tue, 27 Jan 2026 17:42:28 +0100 Subject: [PATCH 03/10] declare the variable --- src/backend/tcop/postgres.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index e1e3b5d3d97..b3a44583eea 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -107,6 +107,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 * ---------------- From 7aa560c4314eaaadc741ddbd26457ed82c65f202 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Wed, 28 Jan 2026 13:44:26 +0100 Subject: [PATCH 04/10] export the variable --- src/include/postgres.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/include/postgres.h b/src/include/postgres.h index 5d5fd7813e8..ec41939303d 100644 --- a/src/include/postgres.h +++ b/src/include/postgres.h @@ -577,3 +577,8 @@ extern Datum Float8GetDatum(float8 X); #endif #endif /* POSTGRES_H */ + +/* Declare for neon use */ +#ifdef USE_INJECTION_POINTS +extern int inside_smgr_api; +#endif \ No newline at end of file From 1bb896288249e25a3ada6fe72b4e755e57cfeb8f Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Fri, 30 Jan 2026 13:35:25 +0100 Subject: [PATCH 05/10] calculate probability --- .../injection_points/injection_points.c | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index f985d9dc415..2e274556633 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -60,6 +60,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; /* @@ -89,7 +91,7 @@ static InjectionPointSharedState *inj_state = NULL; extern PGDLLEXPORT void injection_error(const char *name, const void *private_data); -extern PGDLLEXPORT void injection_error_prob_0_01(const char *name, +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); @@ -188,15 +190,16 @@ injection_error(const char *name, const void *private_data) elog(ERROR, "error triggered for injection point %s", name); } void -injection_error_prob_0_01(const char *name, const void *private_data) +injection_error_prob(const char *name, const void *private_data) { InjectionPointCondition *condition = (InjectionPointCondition *) private_data; if (!injection_point_allowed(condition)) return; - srand((unsigned int)time(NULL)); - if ( rand() % 10000 > 0) + /* Use the probability stored in the condition. */ + double r = (double) rand() / (double) RAND_MAX; + if ( r > condition->prob) return; elog(ERROR, "error triggered for injection point %s", name); @@ -294,8 +297,26 @@ injection_points_attach(PG_FUNCTION_ARGS) function = "injection_notice"; else if (strcmp(action, "wait") == 0) function = "injection_wait"; - else if (strcmp(action, "error-prob-0-01") == 0) - function = "injection_error_prob_0_01"; + else if (strncmp(action, "error-prob-", 11) == 0) + { + const char *p = action + 11; /* points to "0-01" */ + + /* + * Simple parser: convert "0-01" -> "0.01" then strtod(). + * You can make this stricter if you like. + */ + 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'; + + condition.prob = strtod(buf, NULL); + function = "injection_error_prob"; + } else elog(ERROR, "incorrect action \"%s\" for injection point creation", action); From 8a4dd6957427766e4b3597eb792506810bbf7cf5 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Fri, 30 Jan 2026 14:07:35 +0100 Subject: [PATCH 06/10] get rid of variable --- src/test/modules/injection_points/injection_points.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 2e274556633..48261ed7421 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -198,8 +198,7 @@ injection_error_prob(const char *name, const void *private_data) return; /* Use the probability stored in the condition. */ - double r = (double) rand() / (double) RAND_MAX; - if ( r > condition->prob) + if ((double) rand() / (double) RAND_MAX > condition->prob) return; elog(ERROR, "error triggered for injection point %s", name); From 94b69674c9522d501a1ea8a5d3e22c2ca699ed32 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Fri, 30 Jan 2026 17:06:53 +0100 Subject: [PATCH 07/10] move the probability calculation to a function --- .../injection_points/injection_points.c | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 48261ed7421..3c789fb7db7 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -298,22 +298,7 @@ injection_points_attach(PG_FUNCTION_ARGS) function = "injection_wait"; else if (strncmp(action, "error-prob-", 11) == 0) { - const char *p = action + 11; /* points to "0-01" */ - - /* - * Simple parser: convert "0-01" -> "0.01" then strtod(). - * You can make this stricter if you like. - */ - 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'; - - condition.prob = strtod(buf, NULL); + condition.prob = action2prob(action, 11); function = "injection_error_prob"; } else @@ -440,3 +425,29 @@ injection_points_detach(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + +/* + * Coverts the action name into probability + */ +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; +} From 6c12c3429949daf8137e05cee64d3807abf3456f Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Fri, 30 Jan 2026 17:13:52 +0100 Subject: [PATCH 08/10] add a prototype --- src/test/modules/injection_points/injection_points.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index 3c789fb7db7..ed462913d85 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -100,6 +100,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); /* * Callback for shared memory area initialization. @@ -429,7 +430,7 @@ injection_points_detach(PG_FUNCTION_ARGS) /* * Coverts the action name into probability */ -double action2prob(const char *action, const int pos) +static double action2prob(const char *action, const int pos) { /* * Simple parser: convert "0-01" -> "0.01" then strtod(). From 9d8aa6611d615a84f879b1ab683bed71edc1d45c Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Mon, 2 Feb 2026 11:54:47 +0100 Subject: [PATCH 09/10] style fix --- src/include/postgres.h | 2 +- src/test/modules/injection_points/injection_points.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/postgres.h b/src/include/postgres.h index ec41939303d..7d22dc0c946 100644 --- a/src/include/postgres.h +++ b/src/include/postgres.h @@ -581,4 +581,4 @@ extern Datum Float8GetDatum(float8 X); /* Declare for neon use */ #ifdef USE_INJECTION_POINTS extern int inside_smgr_api; -#endif \ No newline at end of file +#endif diff --git a/src/test/modules/injection_points/injection_points.c b/src/test/modules/injection_points/injection_points.c index ed462913d85..e7e386111d3 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -92,7 +92,7 @@ static InjectionPointSharedState *inj_state = NULL; extern PGDLLEXPORT void injection_error(const char *name, const void *private_data); extern PGDLLEXPORT void injection_error_prob(const char *name, - const void *private_data); + const void *private_data); extern PGDLLEXPORT void injection_notice(const char *name, const void *private_data); extern PGDLLEXPORT void injection_wait(const char *name, From 50fdd258ba697f0583ee5c62f81a7a7a9f0a2616 Mon Sep 17 00:00:00 2001 From: Alexey Masterov Date: Fri, 27 Feb 2026 14:51:15 +0100 Subject: [PATCH 10/10] Move the injection point --- src/backend/tcop/postgres.c | 6 ------ src/include/miscadmin.h | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index b3a44583eea..62f42afa0bd 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -3279,12 +3279,6 @@ ProcessInterrupts(void) /* OK to accept any interrupts now? */ if (InterruptHoldoffCount != 0 || CritSectionCount != 0) return; -#ifdef USE_INJECTION_POINTS - if (inside_smgr_api > 0) - { - INJECTION_POINT("SMGR_API"); - } -#endif InterruptPending = false; retry: diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 5f8e197a125..57bed00ca85 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -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) @@ -123,9 +127,21 @@ extern process_interrupts_callback_t ProcessInterruptsCallback; unlikely(InterruptPending)) #endif +#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"); \ + } 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)