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-2025, 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 4 : 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 30 : delay_execution_planner(Query *parse, const char *query_string, 43 : int cursorOptions, ParamListInfo boundParams, 44 : ExplainState *es) 45 : { 46 : PlannedStmt *result; 47 : 48 : /* Invoke the planner, possibly via a previous hook user */ 49 30 : if (prev_planner_hook) 50 0 : result = prev_planner_hook(parse, query_string, cursorOptions, 51 : boundParams, es); 52 : else 53 30 : result = standard_planner(parse, query_string, cursorOptions, 54 : boundParams, es); 55 : 56 : /* If enabled, delay by taking and releasing the specified lock */ 57 30 : if (post_planning_lock_id != 0) 58 : { 59 30 : DirectFunctionCall1(pg_advisory_lock_int8, 60 : Int64GetDatum((int64) post_planning_lock_id)); 61 30 : 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 30 : AcceptInvalidationMessages(); 69 : } 70 : 71 30 : return result; 72 : } 73 : 74 : /* Module load function */ 75 : void 76 4 : _PG_init(void) 77 : { 78 : /* Set up the GUC to control which lock is used */ 79 4 : 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 4 : MarkGUCPrefixReserved("delay_execution"); 92 : 93 : /* Install our hook */ 94 4 : prev_planner_hook = planner_hook; 95 4 : planner_hook = delay_execution_planner; 96 4 : }