mirror of
https://github.com/postgres/postgres.git
synced 2026-10-25 01:39:28 +03:00
injection_points: Add support for string comparison in conditions
injection_points is extended so as a caller can attach a string value to a point, checked with a value provided at runtime. There are a couple of use cases where this can be useful, to be able to target points for specific object names. PID and string conditions can be combined together, so as a point can be allowed for a specific process and string value at the same time. The condition string is capped at 255 characters. As the attach SQL function now takes an optional condition string, it is no longer STRICT, and rejects NULL inputs. Empty condition strings are also rejected, as they don't make sense. Author: Sami Imseih <samimseih@gmail.com> Reviewed-by: Bertrand Drouvot <bertranddrouvot.pg@gmail.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> Discussion: https://postgr.es/m/CAA5RZ0tsGHu2h6YLnVu4HiK05q+gTE_9WVUAqihW2LSscAYS-g@mail.gmail.com
This commit is contained in:
@@ -307,6 +307,49 @@ SELECT injection_points_detach('TestConditionLocal1');
|
||||
|
||||
(1 row)
|
||||
|
||||
-- String match
|
||||
SELECT injection_points_attach('TestConditionString', 'notice', 'MyString');
|
||||
injection_points_attach
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT injection_points_run('TestConditionString', 'MyString'); -- notice
|
||||
NOTICE: notice triggered for injection point TestConditionString (MyString)
|
||||
injection_points_run
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT injection_points_run('TestConditionString', 'WrongString'); -- nothing
|
||||
injection_points_run
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT injection_points_run('TestConditionString', NULL); -- nothing
|
||||
injection_points_run
|
||||
----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT injection_points_detach('TestConditionString');
|
||||
injection_points_detach
|
||||
-------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- string too long for attach
|
||||
SELECT injection_points_attach('TestConditionTooLong', 'error', repeat('a', 256));
|
||||
ERROR: injection point condition string too long (maximum of 255 characters)
|
||||
-- empty condition string
|
||||
SELECT injection_points_attach('TestConditionEmpty', 'error', '');
|
||||
ERROR: injection point condition string must not be empty
|
||||
-- NULL name or action (strictness)
|
||||
SELECT injection_points_attach(NULL, 'error', 'MyString');
|
||||
ERROR: injection point name must not be null
|
||||
SELECT injection_points_attach('TestConditionNull', NULL, 'MyString');
|
||||
ERROR: injection point action must not be null
|
||||
-- Function variant for attach.
|
||||
SELECT injection_points_attach(repeat('a', 64), 'injection_points',
|
||||
'injection_notice', NULL);
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
-- Attaches the action to the given injection point.
|
||||
--
|
||||
CREATE FUNCTION injection_points_attach(IN point_name TEXT,
|
||||
IN action text)
|
||||
IN action text,
|
||||
IN condition_string text DEFAULT NULL)
|
||||
RETURNS void
|
||||
AS 'MODULE_PATHNAME', 'injection_points_attach'
|
||||
LANGUAGE C STRICT PARALLEL UNSAFE;
|
||||
LANGUAGE C PARALLEL UNSAFE;
|
||||
|
||||
--
|
||||
-- injection_points_attach()
|
||||
|
||||
@@ -151,21 +151,20 @@ injection_init_shmem(void)
|
||||
* otherwise.
|
||||
*/
|
||||
static bool
|
||||
injection_point_allowed(const InjectionPointCondition *condition)
|
||||
injection_point_allowed(const InjectionPointCondition *condition,
|
||||
const char *arg)
|
||||
{
|
||||
bool result = true;
|
||||
/* Does not match the condition PID? */
|
||||
if ((condition->type & INJ_CONDITION_PID) &&
|
||||
MyProcPid != condition->pid)
|
||||
return false;
|
||||
|
||||
switch (condition->type)
|
||||
{
|
||||
case INJ_CONDITION_PID:
|
||||
if (MyProcPid != condition->pid)
|
||||
result = false;
|
||||
break;
|
||||
case INJ_CONDITION_ALWAYS:
|
||||
break;
|
||||
}
|
||||
/* Does not match the condition string? */
|
||||
if ((condition->type & INJ_CONDITION_STRING) &&
|
||||
(arg == NULL || strcmp(condition->str, arg) != 0))
|
||||
return false;
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -197,7 +196,7 @@ injection_error(const char *name, const void *private_data, void *arg)
|
||||
const InjectionPointCondition *condition = private_data;
|
||||
char *argstr = arg;
|
||||
|
||||
if (!injection_point_allowed(condition))
|
||||
if (!injection_point_allowed(condition, argstr))
|
||||
return;
|
||||
|
||||
if (argstr)
|
||||
@@ -213,7 +212,7 @@ injection_notice(const char *name, const void *private_data, void *arg)
|
||||
const InjectionPointCondition *condition = private_data;
|
||||
char *argstr = arg;
|
||||
|
||||
if (!injection_point_allowed(condition))
|
||||
if (!injection_point_allowed(condition, argstr))
|
||||
return;
|
||||
|
||||
if (argstr)
|
||||
@@ -244,12 +243,13 @@ injection_wait(const char *name, const void *private_data, void *arg)
|
||||
int index = -1;
|
||||
uint32 injection_wait_event = 0;
|
||||
const InjectionPointCondition *condition = private_data;
|
||||
char *argstr = arg;
|
||||
int delay_us = 0;
|
||||
|
||||
if (inj_state == NULL)
|
||||
injection_init_shmem();
|
||||
|
||||
if (!injection_point_allowed(condition))
|
||||
if (!injection_point_allowed(condition, argstr))
|
||||
return;
|
||||
|
||||
/*
|
||||
@@ -312,11 +312,23 @@ PG_FUNCTION_INFO_V1(injection_points_attach);
|
||||
Datum
|
||||
injection_points_attach(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
||||
char *action = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
||||
char *name;
|
||||
char *action;
|
||||
char *str;
|
||||
char *function;
|
||||
InjectionPointCondition condition = {0};
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
elog(ERROR, "injection point name must not be null");
|
||||
|
||||
if (PG_ARGISNULL(1))
|
||||
elog(ERROR, "injection point action must not be null");
|
||||
|
||||
name = text_to_cstring(PG_GETARG_TEXT_PP(0));
|
||||
action = text_to_cstring(PG_GETARG_TEXT_PP(1));
|
||||
str = PG_ARGISNULL(2) ? NULL
|
||||
: text_to_cstring(PG_GETARG_TEXT_PP(2));
|
||||
|
||||
if (strcmp(action, "error") == 0)
|
||||
function = "injection_error";
|
||||
else if (strcmp(action, "notice") == 0)
|
||||
@@ -328,10 +340,21 @@ injection_points_attach(PG_FUNCTION_ARGS)
|
||||
|
||||
if (injection_point_local)
|
||||
{
|
||||
condition.type = INJ_CONDITION_PID;
|
||||
condition.type |= INJ_CONDITION_PID;
|
||||
condition.pid = MyProcPid;
|
||||
}
|
||||
|
||||
if (str)
|
||||
{
|
||||
if (str[0] == '\0')
|
||||
elog(ERROR, "injection point condition string must not be empty");
|
||||
if (strlen(str) >= INJ_DATA_MAXLEN)
|
||||
elog(ERROR, "injection point condition string too long (maximum of %d characters)",
|
||||
INJ_DATA_MAXLEN - 1);
|
||||
condition.type |= INJ_CONDITION_STRING;
|
||||
strlcpy(condition.str, str, INJ_DATA_MAXLEN);
|
||||
}
|
||||
|
||||
InjectionPointAttach(name, "injection_points", function, &condition,
|
||||
sizeof(InjectionPointCondition));
|
||||
|
||||
|
||||
@@ -15,10 +15,14 @@
|
||||
#ifndef INJECTION_POINTS_H
|
||||
#define INJECTION_POINTS_H
|
||||
|
||||
/* Maximum length for an injection point condition string */
|
||||
#define INJ_DATA_MAXLEN 256
|
||||
|
||||
typedef enum InjectionPointConditionType
|
||||
{
|
||||
INJ_CONDITION_ALWAYS = 0, /* always run */
|
||||
INJ_CONDITION_PID, /* PID restriction */
|
||||
INJ_CONDITION_NONE = 0, /* no restrictions to apply */
|
||||
INJ_CONDITION_PID = 1 << 0, /* PID restriction */
|
||||
INJ_CONDITION_STRING = 1 << 1, /* string to compare with arg */
|
||||
} InjectionPointConditionType;
|
||||
|
||||
typedef struct InjectionPointCondition
|
||||
@@ -28,6 +32,9 @@ typedef struct InjectionPointCondition
|
||||
|
||||
/* ID of the process where the injection point is allowed to run */
|
||||
int pid;
|
||||
|
||||
/* String to compare with an argument at runtime */
|
||||
char str[INJ_DATA_MAXLEN];
|
||||
} InjectionPointCondition;
|
||||
|
||||
#endif /* INJECTION_POINTS_H */
|
||||
|
||||
@@ -88,6 +88,20 @@ SELECT injection_points_detach('TestConditionError');
|
||||
SELECT injection_points_attach('TestConditionLocal1', 'error');
|
||||
SELECT injection_points_detach('TestConditionLocal1');
|
||||
|
||||
-- String match
|
||||
SELECT injection_points_attach('TestConditionString', 'notice', 'MyString');
|
||||
SELECT injection_points_run('TestConditionString', 'MyString'); -- notice
|
||||
SELECT injection_points_run('TestConditionString', 'WrongString'); -- nothing
|
||||
SELECT injection_points_run('TestConditionString', NULL); -- nothing
|
||||
SELECT injection_points_detach('TestConditionString');
|
||||
-- string too long for attach
|
||||
SELECT injection_points_attach('TestConditionTooLong', 'error', repeat('a', 256));
|
||||
-- empty condition string
|
||||
SELECT injection_points_attach('TestConditionEmpty', 'error', '');
|
||||
-- NULL name or action (strictness)
|
||||
SELECT injection_points_attach(NULL, 'error', 'MyString');
|
||||
SELECT injection_points_attach('TestConditionNull', NULL, 'MyString');
|
||||
|
||||
-- Function variant for attach.
|
||||
SELECT injection_points_attach(repeat('a', 64), 'injection_points',
|
||||
'injection_notice', NULL);
|
||||
|
||||
Reference in New Issue
Block a user