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-2024, 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 : { 45 : PlannedStmt *result; 46 : 47 : /* Invoke the planner, possibly via a previous hook user */ 48 30 : if (prev_planner_hook) 49 0 : result = prev_planner_hook(parse, query_string, cursorOptions, 50 : boundParams); 51 : else 52 30 : result = standard_planner(parse, query_string, cursorOptions, 53 : boundParams); 54 : 55 : /* If enabled, delay by taking and releasing the specified lock */ 56 30 : if (post_planning_lock_id != 0) 57 : { 58 30 : DirectFunctionCall1(pg_advisory_lock_int8, 59 : Int64GetDatum((int64) post_planning_lock_id)); 60 30 : DirectFunctionCall1(pg_advisory_unlock_int8, 61 : Int64GetDatum((int64) post_planning_lock_id)); 62 : 63 : /* 64 : * Ensure that we notice any pending invalidations, since the advisory 65 : * lock functions don't do this. 66 : */ 67 30 : AcceptInvalidationMessages(); 68 : } 69 : 70 30 : return result; 71 : } 72 : 73 : /* Module load function */ 74 : void 75 4 : _PG_init(void) 76 : { 77 : /* Set up the GUC to control which lock is used */ 78 4 : DefineCustomIntVariable("delay_execution.post_planning_lock_id", 79 : "Sets the advisory lock ID to be locked/unlocked after planning.", 80 : "Zero disables the delay.", 81 : &post_planning_lock_id, 82 : 0, 83 : 0, INT_MAX, 84 : PGC_USERSET, 85 : 0, 86 : NULL, 87 : NULL, 88 : NULL); 89 : 90 4 : MarkGUCPrefixReserved("delay_execution"); 91 : 92 : /* Install our hook */ 93 4 : prev_planner_hook = planner_hook; 94 4 : planner_hook = delay_execution_planner; 95 4 : }