Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * delay_execution.c
4 : * Test module to allow delay between parsing and execution of a query.
5 : *
6 : * The delay is implemented by taking and immediately releasing a specified
7 : * advisory lock. If another process has previously taken that lock, the
8 : * current process will be blocked until the lock is released; otherwise,
9 : * there's no effect. This allows an isolationtester script to reliably
10 : * test behaviors where some specified action happens in another backend
11 : * between parsing and execution of any desired query.
12 : *
13 : * Copyright (c) 2020-2026, PostgreSQL Global Development Group
14 : *
15 : * IDENTIFICATION
16 : * src/test/modules/delay_execution/delay_execution.c
17 : *
18 : *-------------------------------------------------------------------------
19 : */
20 :
21 : #include "postgres.h"
22 :
23 : #include <limits.h>
24 :
25 : #include "optimizer/planner.h"
26 : #include "utils/fmgrprotos.h"
27 : #include "utils/guc.h"
28 : #include "utils/inval.h"
29 :
30 :
31 0 : PG_MODULE_MAGIC;
32 :
33 : /* GUC: advisory lock ID to use. Zero disables the feature. */
34 : static int post_planning_lock_id = 0;
35 :
36 : /* Save previous planner hook user to be a good citizen */
37 : static planner_hook_type prev_planner_hook = NULL;
38 :
39 :
40 : /* planner_hook function to provide the desired delay */
41 : static PlannedStmt *
42 0 : delay_execution_planner(Query *parse, const char *query_string,
43 : int cursorOptions, ParamListInfo boundParams,
44 : ExplainState *es)
45 : {
46 0 : PlannedStmt *result;
47 :
48 : /* Invoke the planner, possibly via a previous hook user */
49 0 : if (prev_planner_hook)
50 0 : result = prev_planner_hook(parse, query_string, cursorOptions,
51 0 : boundParams, es);
52 : else
53 0 : result = standard_planner(parse, query_string, cursorOptions,
54 0 : boundParams, es);
55 :
56 : /* If enabled, delay by taking and releasing the specified lock */
57 0 : if (post_planning_lock_id != 0)
58 : {
59 0 : DirectFunctionCall1(pg_advisory_lock_int8,
60 : Int64GetDatum((int64) post_planning_lock_id));
61 0 : DirectFunctionCall1(pg_advisory_unlock_int8,
62 : Int64GetDatum((int64) post_planning_lock_id));
63 :
64 : /*
65 : * Ensure that we notice any pending invalidations, since the advisory
66 : * lock functions don't do this.
67 : */
68 0 : AcceptInvalidationMessages();
69 0 : }
70 :
71 0 : return result;
72 0 : }
73 :
74 : /* Module load function */
75 : void
76 0 : _PG_init(void)
77 : {
78 : /* Set up the GUC to control which lock is used */
79 0 : DefineCustomIntVariable("delay_execution.post_planning_lock_id",
80 : "Sets the advisory lock ID to be locked/unlocked after planning.",
81 : "Zero disables the delay.",
82 : &post_planning_lock_id,
83 : 0,
84 : 0, INT_MAX,
85 : PGC_USERSET,
86 : 0,
87 : NULL,
88 : NULL,
89 : NULL);
90 :
91 0 : MarkGUCPrefixReserved("delay_execution");
92 :
93 : /* Install our hook */
94 0 : prev_planner_hook = planner_hook;
95 0 : planner_hook = delay_execution_planner;
96 0 : }
|