Age Owner Branch data TLA Line data Source code
1 : : /*--------------------------------------------------------------------------
2 : : *
3 : : * injection_points.c
4 : : * Code for testing injection points.
5 : : *
6 : : * Injection points are able to trigger user-defined callbacks in pre-defined
7 : : * code paths.
8 : : *
9 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : * IDENTIFICATION
13 : : * src/test/modules/injection_points/injection_points.c
14 : : *
15 : : * -------------------------------------------------------------------------
16 : : */
17 : :
18 : : #include "postgres.h"
19 : :
20 : : #include "fmgr.h"
21 : : #include "funcapi.h"
22 : : #include "injection_points.h"
23 : : #include "miscadmin.h"
24 : : #include "nodes/pg_list.h"
25 : : #include "nodes/value.h"
26 : : #include "storage/dsm_registry.h"
27 : : #include "storage/ipc.h"
28 : : #include "storage/lwlock.h"
29 : : #include "storage/shmem.h"
30 : : #include "storage/spin.h"
31 : : #include "utils/builtins.h"
32 : : #include "utils/guc.h"
33 : : #include "utils/injection_point.h"
34 : : #include "utils/memutils.h"
35 : : #include "utils/tuplestore.h"
36 : : #include "utils/wait_event.h"
37 : :
994 michael@paquier.xyz 38 :CBC 289 : PG_MODULE_MAGIC;
39 : :
40 : : /* Maximum number of waits usable in injection points at once */
41 : : #define INJ_MAX_WAIT 8
42 : : #define INJ_NAME_MAXLEN 64
43 : :
44 : : /* Thresholds of waits */
45 : : #define INJ_WAIT_INITIAL_US 10 /* 10us */
46 : : #define INJ_WAIT_MAX_US 100000 /* 100ms */
47 : :
48 : : /*
49 : : * List of injection points stored in TopMemoryContext attached
50 : : * locally to this process.
51 : : */
52 : : static List *inj_list_local = NIL;
53 : :
54 : : /*
55 : : * Shared state information for injection points.
56 : : *
57 : : * This state data can be initialized in two ways: dynamically with a DSM
58 : : * or when loading the module.
59 : : */
60 : : typedef struct InjectionPointSharedState
61 : : {
62 : : /* Protects access to other fields */
63 : : slock_t lock;
64 : :
65 : : /* Counters advancing when injection_points_wakeup() is called */
66 : : pg_atomic_uint32 wait_counts[INJ_MAX_WAIT];
67 : :
68 : : /* Names of injection points attached to wait counters */
69 : : char name[INJ_MAX_WAIT][INJ_NAME_MAXLEN];
70 : : } InjectionPointSharedState;
71 : :
72 : : /* Pointer to shared-memory state. */
73 : : static InjectionPointSharedState *inj_state = NULL;
74 : :
75 : : extern PGDLLEXPORT void injection_error(const char *name,
76 : : const void *private_data,
77 : : void *arg);
78 : : extern PGDLLEXPORT void injection_notice(const char *name,
79 : : const void *private_data,
80 : : void *arg);
81 : : extern PGDLLEXPORT void injection_wait(const char *name,
82 : : const void *private_data,
83 : : void *arg);
84 : :
85 : : /* track if injection points attached in this process are linked to it */
86 : : static bool injection_point_local = false;
87 : :
88 : : static void injection_shmem_request(void *arg);
89 : : static void injection_shmem_init(void *arg);
90 : :
91 : : static const ShmemCallbacks injection_shmem_callbacks = {
92 : : .request_fn = injection_shmem_request,
93 : : .init_fn = injection_shmem_init,
94 : : };
95 : :
96 : : /*
97 : : * Routine for shared memory area initialization, used as a callback
98 : : * when initializing dynamically with a DSM or when loading the module.
99 : : */
100 : : static void
301 nathan@postgresql.or 101 : 25 : injection_point_init_state(void *ptr, void *arg)
102 : : {
952 michael@paquier.xyz 103 : 25 : InjectionPointSharedState *state = (InjectionPointSharedState *) ptr;
104 : :
105 : 25 : SpinLockInit(&state->lock);
106 : 25 : memset(state->name, 0, sizeof(state->name));
96 michael@paquier.xyz 107 [ + + ]:GNC 225 : for (int i = 0; i < INJ_MAX_WAIT; i++)
108 : 200 : pg_atomic_init_u32(&state->wait_counts[i], 0);
952 michael@paquier.xyz 109 :CBC 25 : }
110 : :
111 : : static void
189 heikki.linnakangas@i 112 : 3 : injection_shmem_request(void *arg)
113 : : {
114 : 3 : ShmemRequestStruct(.name = "injection_points",
115 : : .size = sizeof(InjectionPointSharedState),
116 : : .ptr = (void **) &inj_state,
117 : : );
780 michael@paquier.xyz 118 : 3 : }
119 : :
120 : : static void
189 heikki.linnakangas@i 121 : 3 : injection_shmem_init(void *arg)
122 : : {
123 : : /*
124 : : * First time through, so initialize. This is shared with the dynamic
125 : : * initialization using a DSM.
126 : : */
127 : 3 : injection_point_init_state(inj_state, NULL);
780 michael@paquier.xyz 128 : 3 : }
129 : :
130 : : /*
131 : : * Initialize shared memory area for this module through DSM.
132 : : */
133 : : static void
952 134 : 174 : injection_init_shmem(void)
135 : : {
136 : : bool found;
137 : :
138 [ - + ]: 174 : if (inj_state != NULL)
952 michael@paquier.xyz 139 :UBC 0 : return;
140 : :
952 michael@paquier.xyz 141 :CBC 174 : inj_state = GetNamedDSMSegment("injection_points",
142 : : sizeof(InjectionPointSharedState),
143 : : injection_point_init_state,
144 : : &found, NULL);
145 : : }
146 : :
147 : : /*
148 : : * Check runtime conditions associated to an injection point.
149 : : *
150 : : * Returns true if the named injection point is allowed to run, and false
151 : : * otherwise.
152 : : */
153 : : static bool
59 michael@paquier.xyz 154 :GNC 327 : injection_point_allowed(const InjectionPointCondition *condition,
155 : : const char *arg)
156 : : {
157 : : /* Does not match the condition PID? */
158 [ + + ]: 327 : if ((condition->type & INJ_CONDITION_PID) &&
159 [ + + ]: 275 : MyProcPid != condition->pid)
160 : 126 : return false;
161 : :
162 : : /* Does not match the condition string? */
163 [ + + + + ]: 201 : if ((condition->type & INJ_CONDITION_STRING) &&
164 [ + + ]: 5 : (arg == NULL || strcmp(condition->str, arg) != 0))
165 : 2 : return false;
166 : :
167 : 199 : return true;
168 : : }
169 : :
170 : : /*
171 : : * before_shmem_exit callback to remove injection points linked to a
172 : : * specific process.
173 : : */
174 : : static void
917 michael@paquier.xyz 175 :CBC 88 : injection_points_cleanup(int code, Datum arg)
176 : : {
177 : : ListCell *lc;
178 : :
179 : : /* Leave if nothing is tracked locally */
180 [ - + ]: 88 : if (!injection_point_local)
917 michael@paquier.xyz 181 :UBC 0 : return;
182 : :
183 : : /* Detach all the local points */
883 michael@paquier.xyz 184 [ + + + + :CBC 252 : foreach(lc, inj_list_local)
+ + ]
185 : : {
186 : 164 : char *name = strVal(lfirst(lc));
187 : :
188 : 164 : (void) InjectionPointDetach(name);
189 : : }
190 : : }
191 : :
192 : : /* Set of callbacks available to be attached to an injection point. */
193 : : void
520 194 : 16 : injection_error(const char *name, const void *private_data, void *arg)
195 : : {
273 peter@eisentraut.org 196 : 16 : const InjectionPointCondition *condition = private_data;
197 : 16 : char *argstr = arg;
198 : :
59 michael@paquier.xyz 199 [ - + ]:GNC 16 : if (!injection_point_allowed(condition, argstr))
917 michael@paquier.xyz 200 :UBC 0 : return;
201 : :
520 michael@paquier.xyz 202 [ + + ]:CBC 16 : if (argstr)
203 [ + - ]: 1 : elog(ERROR, "error triggered for injection point %s (%s)",
204 : : name, argstr);
205 : : else
206 [ + - ]: 15 : elog(ERROR, "error triggered for injection point %s", name);
207 : : }
208 : :
209 : : void
210 : 78 : injection_notice(const char *name, const void *private_data, void *arg)
211 : : {
273 peter@eisentraut.org 212 : 78 : const InjectionPointCondition *condition = private_data;
213 : 78 : char *argstr = arg;
214 : :
59 michael@paquier.xyz 215 [ + + ]:GNC 78 : if (!injection_point_allowed(condition, argstr))
917 michael@paquier.xyz 216 :GBC 2 : return;
217 : :
520 michael@paquier.xyz 218 [ + + ]:CBC 76 : if (argstr)
219 [ + + ]: 4 : elog(NOTICE, "notice triggered for injection point %s (%s)",
220 : : name, argstr);
221 : : else
222 [ + - ]: 72 : elog(NOTICE, "notice triggered for injection point %s", name);
223 : : }
224 : :
225 : : /*
226 : : * Error cleanup callback for injection point waits.
227 : : */
228 : : static void
81 229 : 107 : injection_wait_cleanup(int code, Datum arg)
230 : : {
231 : 107 : int index = DatumGetInt32(arg);
232 : :
233 : 107 : SpinLockAcquire(&inj_state->lock);
234 : 107 : inj_state->name[index][0] = '\0';
235 : 107 : SpinLockRelease(&inj_state->lock);
236 : 107 : }
237 : :
238 : : /* Wait until injection_points_wakeup() is called */
239 : : void
520 240 : 233 : injection_wait(const char *name, const void *private_data, void *arg)
241 : : {
952 242 : 233 : uint32 old_wait_counts = 0;
243 : 233 : int index = -1;
244 : 233 : uint32 injection_wait_event = 0;
273 peter@eisentraut.org 245 : 233 : const InjectionPointCondition *condition = private_data;
59 michael@paquier.xyz 246 :GNC 233 : char *argstr = arg;
96 247 : 233 : int delay_us = 0;
248 : :
249 : : /*
250 : : * Check the condition before attaching to the shared state: attaching
251 : : * allocates memory, which a process that is not meant to wait here must
252 : : * not do if the injection point is in a critical section.
253 : : */
59 254 [ + + ]: 233 : if (!injection_point_allowed(condition, argstr))
917 michael@paquier.xyz 255 :CBC 126 : return;
256 : :
18 alvherre@kurilemu.de 257 [ + + ]: 107 : if (inj_state == NULL)
258 : 22 : injection_init_shmem();
259 : :
260 : : /*
261 : : * Use the injection point name for this custom wait event. Note that
262 : : * this custom wait event name is not released, but we don't care much for
263 : : * testing as this should be short-lived.
264 : : */
837 noah@leadboat.com 265 : 107 : injection_wait_event = WaitEventInjectionPointNew(name);
266 : :
267 : : /*
268 : : * Find a free slot to wait for, and register this injection point's name.
269 : : */
952 michael@paquier.xyz 270 : 107 : SpinLockAcquire(&inj_state->lock);
271 [ + - ]: 145 : for (int i = 0; i < INJ_MAX_WAIT; i++)
272 : : {
273 [ + + ]: 145 : if (inj_state->name[i][0] == '\0')
274 : : {
275 : 107 : index = i;
276 : 107 : strlcpy(inj_state->name[i], name, INJ_NAME_MAXLEN);
96 michael@paquier.xyz 277 :GNC 107 : old_wait_counts = pg_atomic_read_u32(&inj_state->wait_counts[i]);
952 michael@paquier.xyz 278 :CBC 107 : break;
279 : : }
280 : : }
281 : 107 : SpinLockRelease(&inj_state->lock);
282 : :
283 [ - + ]: 107 : if (index < 0)
96 michael@paquier.xyz 284 [ # # ]:UNC 0 : elog(ERROR, "could not find free slot for wait of injection point %s",
285 : : name);
286 : :
287 : : /*
288 : : * Wait until the counter is bumped by injection_points_wakeup().
289 : : *
290 : : * This loop starts with a short delay for responsiveness, enlarged to
291 : : * ease the CPU workload in slower environments.
292 : : */
96 michael@paquier.xyz 293 :GNC 107 : delay_us = INJ_WAIT_INITIAL_US;
294 : :
295 : 107 : pgstat_report_wait_start(injection_wait_event);
81 michael@paquier.xyz 296 [ + + ]:CBC 107 : PG_ENSURE_ERROR_CLEANUP(injection_wait_cleanup, Int32GetDatum(index));
297 : : {
81 michael@paquier.xyz 298 [ + + ]:GNC 1651 : while (pg_atomic_read_u32(&inj_state->wait_counts[index]) == old_wait_counts)
81 michael@paquier.xyz 299 :ECB (139) : {
81 michael@paquier.xyz 300 [ + + ]:GNC 1549 : CHECK_FOR_INTERRUPTS();
301 : 1544 : pg_usleep(delay_us);
302 [ + + ]: 1544 : if (delay_us < INJ_WAIT_MAX_US)
303 : 1437 : delay_us = Min(delay_us * 2, INJ_WAIT_MAX_US);
304 : : }
305 : : }
81 michael@paquier.xyz 306 [ - + ]:CBC 105 : PG_END_ENSURE_ERROR_CLEANUP(injection_wait_cleanup, Int32GetDatum(index));
96 michael@paquier.xyz 307 :GNC 102 : pgstat_report_wait_end();
308 : :
309 : : /* Remove this injection point from the waiters. */
81 michael@paquier.xyz 310 :CBC 102 : injection_wait_cleanup(0, Int32GetDatum(index));
311 : : }
312 : :
313 : : /*
314 : : * SQL function for creating an injection point.
315 : : */
994 316 : 206 : PG_FUNCTION_INFO_V1(injection_points_attach);
317 : : Datum
318 : 174 : injection_points_attach(PG_FUNCTION_ARGS)
319 : : {
320 : : char *name;
321 : : char *action;
322 : : char *str;
323 : : char *function;
883 324 : 174 : InjectionPointCondition condition = {0};
325 : :
59 michael@paquier.xyz 326 [ + + ]:GNC 174 : if (PG_ARGISNULL(0))
327 [ + - ]: 1 : elog(ERROR, "injection point name must not be null");
328 : :
329 [ + + ]: 173 : if (PG_ARGISNULL(1))
330 [ + - ]: 1 : elog(ERROR, "injection point action must not be null");
331 : :
332 : 172 : name = text_to_cstring(PG_GETARG_TEXT_PP(0));
333 : 172 : action = text_to_cstring(PG_GETARG_TEXT_PP(1));
334 : 344 : str = PG_ARGISNULL(2) ? NULL
335 [ + + ]: 172 : : text_to_cstring(PG_GETARG_TEXT_PP(2));
336 : :
994 michael@paquier.xyz 337 [ + + ]:CBC 172 : if (strcmp(action, "error") == 0)
338 : 23 : function = "injection_error";
339 [ + + ]: 149 : else if (strcmp(action, "notice") == 0)
340 : 33 : function = "injection_notice";
952 341 [ + + ]: 116 : else if (strcmp(action, "wait") == 0)
342 : 115 : function = "injection_wait";
343 : : else
994 344 [ + - ]: 1 : elog(ERROR, "incorrect action \"%s\" for injection point creation", action);
345 : :
917 346 [ + + ]: 171 : if (injection_point_local)
347 : : {
59 michael@paquier.xyz 348 :GNC 120 : condition.type |= INJ_CONDITION_PID;
883 michael@paquier.xyz 349 :CBC 120 : condition.pid = MyProcPid;
350 : : }
351 : :
59 michael@paquier.xyz 352 [ + + ]:GNC 171 : if (str)
353 : : {
354 [ + + ]: 6 : if (str[0] == '\0')
355 [ + - ]: 1 : elog(ERROR, "injection point condition string must not be empty");
356 [ + + ]: 5 : if (strlen(str) >= INJ_DATA_MAXLEN)
357 [ + - ]: 1 : elog(ERROR, "injection point condition string too long (maximum of %d characters)",
358 : : INJ_DATA_MAXLEN - 1);
359 : 4 : condition.type |= INJ_CONDITION_STRING;
360 : 4 : strlcpy(condition.str, str, INJ_DATA_MAXLEN);
361 : : }
362 : :
883 michael@paquier.xyz 363 :CBC 169 : InjectionPointAttach(name, "injection_points", function, &condition,
364 : : sizeof(InjectionPointCondition));
365 : :
366 [ + + ]: 169 : if (injection_point_local)
367 : : {
368 : : MemoryContext oldctx;
369 : :
370 : : /* Local injection point, so track it for automated cleanup */
371 : 120 : oldctx = MemoryContextSwitchTo(TopMemoryContext);
372 : 120 : inj_list_local = lappend(inj_list_local, makeString(pstrdup(name)));
373 : 120 : MemoryContextSwitchTo(oldctx);
374 : : }
375 : :
994 376 : 169 : PG_RETURN_VOID();
377 : : }
378 : :
379 : : /*
380 : : * SQL function for creating an injection point with library name, function
381 : : * name and private data.
382 : : */
336 383 : 75 : PG_FUNCTION_INFO_V1(injection_points_attach_func);
384 : : Datum
385 : 8 : injection_points_attach_func(PG_FUNCTION_ARGS)
386 : : {
387 : : char *name;
388 : : char *lib_name;
389 : : char *function;
390 : 8 : bytea *private_data = NULL;
391 : 8 : int private_data_size = 0;
392 : :
393 [ + + ]: 8 : if (PG_ARGISNULL(0))
394 [ + - ]: 1 : elog(ERROR, "injection point name cannot be NULL");
395 [ + + ]: 7 : if (PG_ARGISNULL(1))
396 [ + - ]: 1 : elog(ERROR, "injection point library cannot be NULL");
397 [ + + ]: 6 : if (PG_ARGISNULL(2))
398 [ + - ]: 1 : elog(ERROR, "injection point function cannot be NULL");
399 : :
400 : 5 : name = text_to_cstring(PG_GETARG_TEXT_PP(0));
401 : 5 : lib_name = text_to_cstring(PG_GETARG_TEXT_PP(1));
402 : 5 : function = text_to_cstring(PG_GETARG_TEXT_PP(2));
403 : :
404 [ + + ]: 5 : if (!PG_ARGISNULL(3))
405 : : {
406 : 1 : private_data = PG_GETARG_BYTEA_PP(3);
407 : 1 : private_data_size = VARSIZE_ANY_EXHDR(private_data);
408 : : }
409 : :
410 [ + + ]: 5 : if (private_data != NULL)
411 : 1 : InjectionPointAttach(name, lib_name, function, VARDATA_ANY(private_data),
412 : : private_data_size);
413 : : else
414 : 4 : InjectionPointAttach(name, lib_name, function, NULL,
415 : : 0);
416 : 1 : PG_RETURN_VOID();
417 : : }
418 : :
419 : : /*
420 : : * SQL function for loading an injection point.
421 : : */
829 422 : 75 : PG_FUNCTION_INFO_V1(injection_points_load);
423 : : Datum
424 : 2 : injection_points_load(PG_FUNCTION_ARGS)
425 : : {
426 : 2 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
427 : :
428 [ + + ]: 2 : if (inj_state == NULL)
429 : 1 : injection_init_shmem();
430 : :
431 : 2 : INJECTION_POINT_LOAD(name);
432 : :
433 : 2 : PG_RETURN_VOID();
434 : : }
435 : :
436 : : /*
437 : : * SQL function for triggering an injection point.
438 : : */
994 439 : 84 : PG_FUNCTION_INFO_V1(injection_points_run);
440 : : Datum
441 : 35 : injection_points_run(PG_FUNCTION_ARGS)
442 : : {
443 : : char *name;
520 444 : 35 : char *arg = NULL;
445 : :
446 [ + + ]: 35 : if (PG_ARGISNULL(0))
447 : 1 : PG_RETURN_VOID();
448 : 34 : name = text_to_cstring(PG_GETARG_TEXT_PP(0));
449 : :
450 [ + + ]: 34 : if (!PG_ARGISNULL(1))
451 : 4 : arg = text_to_cstring(PG_GETARG_TEXT_PP(1));
452 : :
453 : 34 : INJECTION_POINT(name, arg);
454 : :
994 455 : 26 : PG_RETURN_VOID();
456 : : }
457 : :
458 : : /*
459 : : * SQL function for triggering an injection point from cache.
460 : : */
816 461 : 76 : PG_FUNCTION_INFO_V1(injection_points_cached);
462 : : Datum
463 : 5 : injection_points_cached(PG_FUNCTION_ARGS)
464 : : {
465 : : char *name;
520 466 : 5 : char *arg = NULL;
467 : :
468 [ + + ]: 5 : if (PG_ARGISNULL(0))
469 : 1 : PG_RETURN_VOID();
470 : 4 : name = text_to_cstring(PG_GETARG_TEXT_PP(0));
471 : :
472 [ + + ]: 4 : if (!PG_ARGISNULL(1))
473 : 1 : arg = text_to_cstring(PG_GETARG_TEXT_PP(1));
474 : :
475 : 4 : INJECTION_POINT_CACHED(name, arg);
476 : :
816 477 : 4 : PG_RETURN_VOID();
478 : : }
479 : :
480 : : /*
481 : : * SQL function for waking up an injection point waiting in injection_wait().
482 : : */
952 483 : 181 : PG_FUNCTION_INFO_V1(injection_points_wakeup);
484 : : Datum
485 : 113 : injection_points_wakeup(PG_FUNCTION_ARGS)
486 : : {
487 : 113 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
488 : 113 : int index = -1;
489 : :
490 [ + + ]: 113 : if (inj_state == NULL)
491 : 85 : injection_init_shmem();
492 : :
493 : : /* Find the injection point then bump its wait counter */
494 : 113 : SpinLockAcquire(&inj_state->lock);
495 [ + + ]: 162 : for (int i = 0; i < INJ_MAX_WAIT; i++)
496 : : {
497 [ + + ]: 161 : if (strcmp(name, inj_state->name[i]) == 0)
498 : : {
499 : 112 : index = i;
500 : 112 : break;
501 : : }
502 : : }
503 [ + + ]: 113 : if (index < 0)
504 : : {
505 : 1 : SpinLockRelease(&inj_state->lock);
506 [ + - ]: 1 : elog(ERROR, "could not find injection point %s to wake up", name);
507 : : }
508 : 112 : SpinLockRelease(&inj_state->lock);
509 : :
96 michael@paquier.xyz 510 :GNC 112 : pg_atomic_fetch_add_u32(&inj_state->wait_counts[index], 1);
952 michael@paquier.xyz 511 :CBC 112 : PG_RETURN_VOID();
512 : : }
513 : :
514 : : /*
515 : : * injection_points_set_local
516 : : *
517 : : * Track if any injection point created in this process ought to run only
518 : : * in this process. Such injection points are detached automatically when
519 : : * this process exits. This is useful to make test suites concurrent-safe.
520 : : */
917 521 : 162 : PG_FUNCTION_INFO_V1(injection_points_set_local);
522 : : Datum
523 : 88 : injection_points_set_local(PG_FUNCTION_ARGS)
524 : : {
525 : : /* Enable flag to add a runtime condition based on this process ID */
526 : 88 : injection_point_local = true;
527 : :
528 [ + + ]: 88 : if (inj_state == NULL)
529 : 66 : injection_init_shmem();
530 : :
531 : : /*
532 : : * Register a before_shmem_exit callback to remove any injection points
533 : : * linked to this process.
534 : : */
535 : 88 : before_shmem_exit(injection_points_cleanup, (Datum) 0);
536 : :
537 : 88 : PG_RETURN_VOID();
538 : : }
539 : :
540 : : /*
541 : : * SQL function for dropping an injection point.
542 : : */
994 543 : 190 : PG_FUNCTION_INFO_V1(injection_points_detach);
544 : : Datum
545 : 150 : injection_points_detach(PG_FUNCTION_ARGS)
546 : : {
547 : 150 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0));
548 : :
883 549 [ + + ]: 150 : if (!InjectionPointDetach(name))
550 [ + - ]: 1 : elog(ERROR, "could not detach injection point \"%s\"", name);
551 : :
552 : : /* Remove point from local list, if required */
553 [ + + ]: 149 : if (inj_list_local != NIL)
554 : : {
555 : : MemoryContext oldctx;
556 : :
557 : 39 : oldctx = MemoryContextSwitchTo(TopMemoryContext);
558 : 39 : inj_list_local = list_delete(inj_list_local, makeString(name));
559 : 39 : MemoryContextSwitchTo(oldctx);
560 : : }
561 : :
994 562 : 149 : PG_RETURN_VOID();
563 : : }
564 : :
565 : : /*
566 : : * SQL function for listing all the injection points attached.
567 : : */
459 568 : 76 : PG_FUNCTION_INFO_V1(injection_points_list);
569 : : Datum
570 : 3 : injection_points_list(PG_FUNCTION_ARGS)
571 : : {
572 : : #define NUM_INJECTION_POINTS_LIST 3
573 : 3 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
574 : : List *inj_points;
575 : : ListCell *lc;
576 : :
577 : : /* Build a tuplestore to return our results in */
578 : 3 : InitMaterializedSRF(fcinfo, 0);
579 : :
580 : 3 : inj_points = InjectionPointList();
581 : :
582 [ + + + + : 7 : foreach(lc, inj_points)
+ + ]
583 : : {
584 : : Datum values[NUM_INJECTION_POINTS_LIST];
585 : : bool nulls[NUM_INJECTION_POINTS_LIST];
586 : 4 : InjectionPointData *inj_point = lfirst(lc);
587 : :
588 : 4 : memset(values, 0, sizeof(values));
589 : 4 : memset(nulls, 0, sizeof(nulls));
590 : :
591 : 4 : values[0] = PointerGetDatum(cstring_to_text(inj_point->name));
592 : 4 : values[1] = PointerGetDatum(cstring_to_text(inj_point->library));
593 : 4 : values[2] = PointerGetDatum(cstring_to_text(inj_point->function));
594 : :
595 : : /* shove row into tuplestore */
596 : 4 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
597 : : }
598 : :
599 : 3 : return (Datum) 0;
600 : : #undef NUM_INJECTION_POINTS_LIST
601 : : }
602 : :
603 : : void
798 604 : 289 : _PG_init(void)
605 : : {
606 [ + + ]: 289 : if (!process_shared_preload_libraries_in_progress)
607 : 286 : return;
608 : :
189 heikki.linnakangas@i 609 : 3 : RegisterShmemCallbacks(&injection_shmem_callbacks);
610 : : }
|