diff --git a/.travis.yml b/.travis.yml index dc651bd..05f37c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ notifications: on_failure: always env: + - PG_VERSION=12 LEVEL=hardcore + - PG_VERSION=12 - PG_VERSION=11 LEVEL=hardcore - PG_VERSION=11 - PG_VERSION=10 LEVEL=hardcore @@ -28,4 +30,4 @@ env: matrix: allow_failures: - env: PG_VERSION=10 LEVEL=nightmare - - env: PG_VERSION=9.6 LEVEL=nightmare \ No newline at end of file + - env: PG_VERSION=9.6 LEVEL=nightmare diff --git a/Dockerfile.tmpl b/Dockerfile.tmpl index 43d3691..1725de3 100644 --- a/Dockerfile.tmpl +++ b/Dockerfile.tmpl @@ -6,7 +6,7 @@ RUN apk add --no-cache \ perl perl-ipc-run \ make musl-dev gcc bison flex coreutils \ zlib-dev libedit-dev \ - clang clang-analyzer \ + clang clang-analyzer linux-headers \ python2 python2-dev py2-virtualenv; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1bafabf --- /dev/null +++ b/LICENSE @@ -0,0 +1,11 @@ +pg_query_state is released under the PostgreSQL License, a liberal Open Source license, similar to the BSD or MIT licenses. + +Copyright (c) 2016-2019, Postgres Professional +Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group +Portions Copyright (c) 1994, The Regents of the University of California + +Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL POSTGRES PROFESSIONAL BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF POSTGRES PROFESSIONAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +POSTGRES PROFESSIONAL SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND POSTGRES PROFESSIONAL HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/Makefile b/Makefile index 08d24ac..c142ed5 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ EXTRA_CLEAN = ./isolation_output $(EXTENSION)--$(EXTVERSION).sql \ Dockerfile ./tests/*.pyc ifdef USE_PGXS -PG_CONFIG = pg_config +PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) else diff --git a/README.md b/README.md index 34ecdb0..9baadd5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Build Status](https://travis-ci.org/postgrespro/pg_query_state.svg?branch=master)](https://travis-ci.org/postgrespro/pg_query_state) +[![codecov](https://codecov.io/gh/postgrespro/pg_query_state/branch/master/graph/badge.svg)](https://codecov.io/gh/postgrespro/pg_query_state) # pg\_query\_state The `pg_query_state` module provides facility to know the current state of query execution on working backend. To enable this extension you have to patch the latest stable version of PostgreSQL. Different branches are intended for different version numbers of PostgreSQL, e.g., branch _PG9_5_ corresponds to PostgreSQL 9.5. diff --git a/expected/corner_cases_2.out b/expected/corner_cases_2.out new file mode 100644 index 0000000..da624f7 --- /dev/null +++ b/expected/corner_cases_2.out @@ -0,0 +1,64 @@ +Parsed test spec with 2 sessions + +starting permutation: s1_pg_qs_1 +step s1_pg_qs_1: select pg_query_state(1); +ERROR: backend with pid=1 not found + +starting permutation: s1_pg_qs_2 +step s1_pg_qs_2: select pg_query_state(pg_backend_pid()); +ERROR: attempt to extract state of current process + +starting permutation: s1_save_pid s2_pg_qs_counterpart +step s1_save_pid: select save_own_pid(0); +save_own_pid + + +s2: INFO: state of backend is idle +step s2_pg_qs_counterpart: select pg_query_state(counterpart_pid(0)); +pg_query_state + + +starting permutation: s1_save_pid s1_disable_pg_qs s2_pg_qs_counterpart +step s1_save_pid: select save_own_pid(0); +save_own_pid + + +step s1_disable_pg_qs: set pg_query_state.enable to off; +s2: INFO: query execution statistics disabled +step s2_pg_qs_counterpart: select pg_query_state(counterpart_pid(0)); +pg_query_state + + +starting permutation: s1_set_bob s2_set_bob s1_save_pid s2_pg_qs_counterpart +step s1_set_bob: set role bob; +step s2_set_bob: set role bob; +step s1_save_pid: select save_own_pid(0); +save_own_pid + + +s2: INFO: state of backend is idle +step s2_pg_qs_counterpart: select pg_query_state(counterpart_pid(0)); +pg_query_state + + +starting permutation: s1_set_bob s2_set_su s1_save_pid s2_pg_qs_counterpart +step s1_set_bob: set role bob; +step s2_set_su: set role super; +step s1_save_pid: select save_own_pid(0); +save_own_pid + + +s2: INFO: state of backend is idle +step s2_pg_qs_counterpart: select pg_query_state(counterpart_pid(0)); +pg_query_state + + +starting permutation: s1_set_bob s2_set_alice s1_save_pid s2_pg_qs_counterpart +step s1_set_bob: set role bob; +step s2_set_alice: set role alice; +step s1_save_pid: select save_own_pid(0); +save_own_pid + + +step s2_pg_qs_counterpart: select pg_query_state(counterpart_pid(0)); +ERROR: permission denied diff --git a/patches/custom_signals_12.0.patch b/patches/custom_signals_12.0.patch new file mode 100644 index 0000000..7c57130 --- /dev/null +++ b/patches/custom_signals_12.0.patch @@ -0,0 +1,196 @@ +diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c +index 7605b2c367..6a4327fe76 100644 +--- a/src/backend/storage/ipc/procsignal.c ++++ b/src/backend/storage/ipc/procsignal.c +@@ -60,12 +60,20 @@ typedef struct + */ + #define NumProcSignalSlots (MaxBackends + NUM_AUXPROCTYPES) + ++#define IsCustomProcSignalReason(reason) \ ++ ((reason) >= PROCSIG_CUSTOM_1 && (reason) <= PROCSIG_CUSTOM_N) ++ ++static bool CustomSignalPendings[NUM_CUSTOM_PROCSIGNALS]; ++static ProcSignalHandler_type CustomInterruptHandlers[NUM_CUSTOM_PROCSIGNALS]; ++ + static ProcSignalSlot *ProcSignalSlots = NULL; + static volatile ProcSignalSlot *MyProcSignalSlot = NULL; + + static bool CheckProcSignal(ProcSignalReason reason); + static void CleanupProcSignalState(int status, Datum arg); + ++static void CheckAndSetCustomSignalInterrupts(void); ++ + /* + * ProcSignalShmemSize + * Compute space needed for procsignal's shared memory +@@ -165,6 +173,36 @@ CleanupProcSignalState(int status, Datum arg) + slot->pss_pid = 0; + } + ++/* ++ * RegisterCustomProcSignalHandler ++ * Assign specific handler of custom process signal with new ++ * ProcSignalReason key. ++ * ++ * This function has to be called in _PG_init function of extensions at the ++ * stage of loading shared preloaded libraries. Otherwise it throws fatal error. ++ * ++ * Return INVALID_PROCSIGNAL if all slots for custom signals are occupied. ++ */ ++ProcSignalReason ++RegisterCustomProcSignalHandler(ProcSignalHandler_type handler) ++{ ++ ProcSignalReason reason; ++ ++ if (!process_shared_preload_libraries_in_progress) ++ ereport(FATAL, (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("cannot register custom signal after startup"))); ++ ++ /* Iterate through custom signal slots to find a free one */ ++ for (reason = PROCSIG_CUSTOM_1; reason <= PROCSIG_CUSTOM_N; reason++) ++ if (!CustomInterruptHandlers[reason - PROCSIG_CUSTOM_1]) ++ { ++ CustomInterruptHandlers[reason - PROCSIG_CUSTOM_1] = handler; ++ return reason; ++ } ++ ++ return INVALID_PROCSIGNAL; ++} ++ + /* + * SendProcSignal + * Send a signal to a Postgres process +@@ -292,9 +330,63 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) + if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN)) + RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + ++ CheckAndSetCustomSignalInterrupts(); ++ + SetLatch(MyLatch); + + latch_sigusr1_handler(); + + errno = save_errno; + } ++ ++/* ++ * Handle receipt of an interrupt indicating any of custom process signals. ++ */ ++static void ++CheckAndSetCustomSignalInterrupts() ++{ ++ ProcSignalReason reason; ++ ++ for (reason = PROCSIG_CUSTOM_1; reason <= PROCSIG_CUSTOM_N; reason++) ++ { ++ if (CheckProcSignal(reason)) ++ { ++ ++ /* set interrupt flags */ ++ InterruptPending = true; ++ CustomSignalPendings[reason - PROCSIG_CUSTOM_1] = true; ++ } ++ } ++ ++ SetLatch(MyLatch); ++} ++ ++/* ++ * CheckAndHandleCustomSignals ++ * Check custom signal flags and call handler assigned to that signal ++ * if it is not NULL ++ * ++ * This function is called within CHECK_FOR_INTERRUPTS if interrupt occurred. ++ */ ++void ++CheckAndHandleCustomSignals(void) ++{ ++ int i; ++ ++ /* Disable interrupts to avoid recursive calls */ ++ HOLD_INTERRUPTS(); ++ ++ /* Check on expiring of custom signals and call its handlers if exist */ ++ for (i = 0; i < NUM_CUSTOM_PROCSIGNALS; i++) ++ if (CustomSignalPendings[i]) ++ { ++ ProcSignalHandler_type handler; ++ ++ CustomSignalPendings[i] = false; ++ handler = CustomInterruptHandlers[i]; ++ if (handler != NULL) ++ handler(); ++ } ++ ++ RESUME_INTERRUPTS(); ++} +diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c +index c28cc37012..f5a48b98e8 100644 +--- a/src/backend/tcop/postgres.c ++++ b/src/backend/tcop/postgres.c +@@ -3139,6 +3139,8 @@ ProcessInterrupts(void) + + if (ParallelMessagePending) + HandleParallelMessages(); ++ ++ CheckAndHandleCustomSignals(); + } + + +diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h +index 05b186a05c..d961790b7e 100644 +--- a/src/include/storage/procsignal.h ++++ b/src/include/storage/procsignal.h +@@ -17,6 +17,8 @@ + #include "storage/backendid.h" + + ++#define NUM_CUSTOM_PROCSIGNALS 64 ++ + /* + * Reasons for signalling a Postgres child process (a backend or an auxiliary + * process, like checkpointer). We can cope with concurrent signals for different +@@ -29,6 +31,8 @@ + */ + typedef enum + { ++ INVALID_PROCSIGNAL = -1, /* Must be first */ ++ + PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */ + PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */ + PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */ +@@ -42,9 +46,20 @@ typedef enum + PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, + PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, + ++ PROCSIG_CUSTOM_1, ++ /* ++ * PROCSIG_CUSTOM_2, ++ * ..., ++ * PROCSIG_CUSTOM_N-1, ++ */ ++ PROCSIG_CUSTOM_N = PROCSIG_CUSTOM_1 + NUM_CUSTOM_PROCSIGNALS - 1, ++ + NUM_PROCSIGNALS /* Must be last! */ + } ProcSignalReason; + ++/* Handler of custom process signal */ ++typedef void (*ProcSignalHandler_type) (void); ++ + /* + * prototypes for functions in procsignal.c + */ +@@ -52,9 +67,13 @@ extern Size ProcSignalShmemSize(void); + extern void ProcSignalShmemInit(void); + + extern void ProcSignalInit(int pss_idx); ++extern ProcSignalReason ++ RegisterCustomProcSignalHandler(ProcSignalHandler_type handler); + extern int SendProcSignal(pid_t pid, ProcSignalReason reason, + BackendId backendId); + ++extern void CheckAndHandleCustomSignals(void); ++ + extern void procsignal_sigusr1_handler(SIGNAL_ARGS); + + #endif /* PROCSIGNAL_H */ diff --git a/patches/runtime_explain_12.0.patch b/patches/runtime_explain_12.0.patch new file mode 100644 index 0000000..1d105b2 --- /dev/null +++ b/patches/runtime_explain_12.0.patch @@ -0,0 +1,264 @@ +From 8ac7d540edcb2dccc663e73ce7e856273f60fbe6 Mon Sep 17 00:00:00 2001 +From: Alexey Kondratov +Date: Tue, 24 Sep 2019 18:49:11 +0300 +Subject: [PATCH] EXPLAIN: print intermediate state of query execution + +EXPLAIN core patch required for pg_query_state +--- + src/backend/commands/explain.c | 152 +++++++++++++++++++++++++++------ + src/include/commands/explain.h | 2 + + 2 files changed, 129 insertions(+), 25 deletions(-) + +diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c +index 92969636b75..fab4267a2c1 100644 +--- a/src/backend/commands/explain.c ++++ b/src/backend/commands/explain.c +@@ -919,15 +919,36 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es) + Instrumentation *instr = rInfo->ri_TrigInstrument + nt; + char *relname; + char *conname = NULL; ++ instr_time starttimespan; ++ double total; ++ double ntuples; ++ double ncalls; + ++ if (!es->runtime) ++ { + /* Must clean up instrumentation state */ + InstrEndLoop(instr); ++ } ++ ++ /* Collect statistic variables */ ++ if (!INSTR_TIME_IS_ZERO(instr->starttime)) ++ { ++ INSTR_TIME_SET_CURRENT(starttimespan); ++ INSTR_TIME_SUBTRACT(starttimespan, instr->starttime); ++ } ++ else ++ INSTR_TIME_SET_ZERO(starttimespan); ++ ++ total = instr->total + INSTR_TIME_GET_DOUBLE(instr->counter) ++ + INSTR_TIME_GET_DOUBLE(starttimespan); ++ ntuples = instr->ntuples + instr->tuplecount; ++ ncalls = ntuples + !INSTR_TIME_IS_ZERO(starttimespan); + + /* + * We ignore triggers that were never invoked; they likely aren't + * relevant to the current query type. + */ +- if (instr->ntuples == 0) ++ if (ncalls == 0) + continue; + + ExplainOpenGroup("Trigger", NULL, true, es); +@@ -953,9 +974,9 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es) + appendStringInfo(es->str, " on %s", relname); + if (es->timing) + appendStringInfo(es->str, ": time=%.3f calls=%.0f\n", +- 1000.0 * instr->total, instr->ntuples); ++ 1000.0 * total, ncalls); + else +- appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples); ++ appendStringInfo(es->str, ": calls=%.0f\n", ncalls); + } + else + { +@@ -964,9 +985,8 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es) + ExplainPropertyText("Constraint Name", conname, es); + ExplainPropertyText("Relation", relname, es); + if (es->timing) +- ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3, +- es); +- ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es); ++ ExplainPropertyFloat("Time", "ms", 1000.0 * total, 3, es); ++ ExplainPropertyFloat("Calls", NULL, ncalls, 0, es); + } + + if (conname) +@@ -1501,8 +1521,11 @@ ExplainNode(PlanState *planstate, List *ancestors, + * instrumentation results the user didn't ask for. But we do the + * InstrEndLoop call anyway, if possible, to reduce the number of cases + * auto_explain has to contend with. ++ * ++ * If flag es->stateinfo is set, i.e. when printing the current execution ++ * state, this step of cleaning up is missed. + */ +- if (planstate->instrument) ++ if (planstate->instrument && !es->runtime) + InstrEndLoop(planstate->instrument); + + if (es->analyze && +@@ -1537,7 +1560,7 @@ ExplainNode(PlanState *planstate, List *ancestors, + ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es); + } + } +- else if (es->analyze) ++ else if (es->analyze && !es->runtime) + { + if (es->format == EXPLAIN_FORMAT_TEXT) + appendStringInfoString(es->str, " (never executed)"); +@@ -1553,6 +1576,75 @@ ExplainNode(PlanState *planstate, List *ancestors, + } + } + ++ /* ++ * Print the progress of node execution at current loop. ++ */ ++ if (planstate->instrument && es->analyze && es->runtime) ++ { ++ instr_time starttimespan; ++ double startup_sec; ++ double total_sec; ++ double rows; ++ double loop_num; ++ bool finished; ++ ++ if (!INSTR_TIME_IS_ZERO(planstate->instrument->starttime)) ++ { ++ INSTR_TIME_SET_CURRENT(starttimespan); ++ INSTR_TIME_SUBTRACT(starttimespan, planstate->instrument->starttime); ++ } ++ else ++ INSTR_TIME_SET_ZERO(starttimespan); ++ startup_sec = 1000.0 * planstate->instrument->firsttuple; ++ total_sec = 1000.0 * (INSTR_TIME_GET_DOUBLE(planstate->instrument->counter) ++ + INSTR_TIME_GET_DOUBLE(starttimespan)); ++ rows = planstate->instrument->tuplecount; ++ loop_num = planstate->instrument->nloops + 1; ++ ++ finished = planstate->instrument->nloops > 0 ++ && !planstate->instrument->running ++ && INSTR_TIME_IS_ZERO(starttimespan); ++ ++ if (!finished) ++ { ++ ExplainOpenGroup("Current loop", "Current loop", true, es); ++ if (es->format == EXPLAIN_FORMAT_TEXT) ++ { ++ if (es->timing) ++ { ++ if (planstate->instrument->running) ++ appendStringInfo(es->str, ++ " (Current loop: actual time=%.3f..%.3f rows=%.0f, loop number=%.0f)", ++ startup_sec, total_sec, rows, loop_num); ++ else ++ appendStringInfo(es->str, ++ " (Current loop: running time=%.3f actual rows=0, loop number=%.0f)", ++ total_sec, loop_num); ++ } ++ else ++ appendStringInfo(es->str, ++ " (Current loop: actual rows=%.0f, loop number=%.0f)", ++ rows, loop_num); ++ } ++ else ++ { ++ ExplainPropertyFloat("Actual Loop Number", NULL, loop_num, 0, es); ++ if (es->timing) ++ { ++ if (planstate->instrument->running) ++ { ++ ExplainPropertyFloat("Actual Startup Time", NULL, startup_sec, 3, es); ++ ExplainPropertyFloat("Actual Total Time", NULL, total_sec, 3, es); ++ } ++ else ++ ExplainPropertyFloat("Running Time", NULL, total_sec, 3, es); ++ } ++ ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es); ++ } ++ ExplainCloseGroup("Current loop", "Current loop", true, es); ++ } ++ } ++ + /* in text format, first line ends here */ + if (es->format == EXPLAIN_FORMAT_TEXT) + appendStringInfoChar(es->str, '\n'); +@@ -1868,8 +1960,9 @@ ExplainNode(PlanState *planstate, List *ancestors, + if (es->buffers && planstate->instrument) + show_buffer_usage(es, &planstate->instrument->bufusage); + +- /* Show worker detail */ +- if (es->analyze && es->verbose && planstate->worker_instrument) ++ /* Show worker detail after query execution */ ++ if (es->analyze && es->verbose && planstate->worker_instrument ++ && !es->runtime) + { + WorkerInstrumentation *w = planstate->worker_instrument; + bool opened_group = false; +@@ -2758,20 +2851,17 @@ show_instrumentation_count(const char *qlabel, int which, + if (!es->analyze || !planstate->instrument) + return; + ++ nloops = planstate->instrument->nloops; + if (which == 2) +- nfiltered = planstate->instrument->nfiltered2; ++ nfiltered = ((nloops > 0) ? planstate->instrument->nfiltered2 / nloops : ++ 0); + else +- nfiltered = planstate->instrument->nfiltered1; +- nloops = planstate->instrument->nloops; ++ nfiltered = ((nloops > 0) ? planstate->instrument->nfiltered1 / nloops : ++ 0); + + /* In text mode, suppress zero counts; they're not interesting enough */ + if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT) +- { +- if (nloops > 0) +- ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es); +- else +- ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es); +- } ++ ExplainPropertyFloat(qlabel, NULL, nfiltered, 0, es); + } + + /* +@@ -3290,15 +3380,27 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors, + double insert_path; + double other_path; + +- InstrEndLoop(mtstate->mt_plans[0]->instrument); ++ if (!es->runtime) ++ InstrEndLoop(mtstate->mt_plans[0]->instrument); + + /* count the number of source rows */ +- total = mtstate->mt_plans[0]->instrument->ntuples; +- other_path = mtstate->ps.instrument->ntuples2; +- insert_path = total - other_path; ++ other_path = mtstate->ps.instrument->nfiltered2; ++ ++ /* ++ * Insert occurs after extracting row from subplan and in runtime mode ++ * we can appear between these two operations - situation when ++ * total > insert_path + other_path. Therefore we don't know exactly ++ * whether last row from subplan is inserted. ++ * We don't print inserted tuples in runtime mode in order to not print ++ * inconsistent data ++ */ ++ if (!es->runtime) ++ { ++ total = mtstate->mt_plans[0]->instrument->ntuples; ++ insert_path = total - other_path; ++ ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es); ++ } + +- ExplainPropertyFloat("Tuples Inserted", NULL, +- insert_path, 0, es); + ExplainPropertyFloat("Conflicting Tuples", NULL, + other_path, 0, es); + } +diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h +index f8b79ec120e..b6e2401065c 100644 +--- a/src/include/commands/explain.h ++++ b/src/include/commands/explain.h +@@ -37,6 +37,8 @@ typedef struct ExplainState + bool summary; /* print total planning and execution timing */ + bool settings; /* print modified settings */ + ExplainFormat format; /* output format */ ++ bool runtime; /* print intermediate state of query execution, ++ not after completion */ + /* state for output formatting --- not reset for each new plan tree */ + int indent; /* current indentation level */ + List *grouping_stack; /* format-specific grouping state */ +-- +2.17.1 + diff --git a/pg_query_state.c b/pg_query_state.c index 9bd6771..55aec13 100644 --- a/pg_query_state.c +++ b/pg_query_state.c @@ -50,7 +50,6 @@ bool pg_qs_buffers = false; static ExecutorStart_hook_type prev_ExecutorStart = NULL; static ExecutorRun_hook_type prev_ExecutorRun = NULL; static ExecutorFinish_hook_type prev_ExecutorFinish = NULL; -static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; static shmem_startup_hook_type prev_shmem_startup_hook = NULL; void _PG_init(void); @@ -65,7 +64,6 @@ static void qs_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once); #endif static void qs_ExecutorFinish(QueryDesc *queryDesc); -static void qs_ExecutorEnd(QueryDesc *queryDesc); /* Global variables */ List *QueryDescStack = NIL; @@ -249,8 +247,6 @@ _PG_init(void) ExecutorRun_hook = qs_ExecutorRun; prev_ExecutorFinish = ExecutorFinish_hook; ExecutorFinish_hook = qs_ExecutorFinish; - prev_ExecutorEnd = ExecutorEnd_hook; - ExecutorEnd_hook = qs_ExecutorEnd; prev_shmem_startup_hook = shmem_startup_hook; shmem_startup_hook = pg_qs_shmem_startup; } @@ -265,13 +261,11 @@ _PG_fini(void) /* clear global state */ list_free(QueryDescStack); - AssignCustomProcSignalHandler(QueryStatePollReason, NULL); /* Uninstall hooks. */ ExecutorStart_hook = prev_ExecutorStart; ExecutorRun_hook = prev_ExecutorRun; ExecutorFinish_hook = prev_ExecutorFinish; - ExecutorEnd_hook = prev_ExecutorEnd; shmem_startup_hook = prev_shmem_startup_hook; } @@ -283,32 +277,20 @@ _PG_fini(void) static void qs_ExecutorStart(QueryDesc *queryDesc, int eflags) { - PG_TRY(); - { - /* Enable per-node instrumentation */ - if (pg_qs_enable && ((eflags & EXEC_FLAG_EXPLAIN_ONLY) == 0)) - { - queryDesc->instrument_options |= INSTRUMENT_ROWS; - if (pg_qs_timing) - queryDesc->instrument_options |= INSTRUMENT_TIMER; - if (pg_qs_buffers) - queryDesc->instrument_options |= INSTRUMENT_BUFFERS; - } - - if (prev_ExecutorStart) - prev_ExecutorStart(queryDesc, eflags); - else - standard_ExecutorStart(queryDesc, eflags); - - /* push structure about current query in global stack */ - QueryDescStack = lcons(queryDesc, QueryDescStack); - } - PG_CATCH(); + /* Enable per-node instrumentation */ + if (pg_qs_enable && ((eflags & EXEC_FLAG_EXPLAIN_ONLY) == 0)) { - QueryDescStack = NIL; - PG_RE_THROW(); + queryDesc->instrument_options |= INSTRUMENT_ROWS; + if (pg_qs_timing) + queryDesc->instrument_options |= INSTRUMENT_TIMER; + if (pg_qs_buffers) + queryDesc->instrument_options |= INSTRUMENT_BUFFERS; } - PG_END_TRY(); + + if (prev_ExecutorStart) + prev_ExecutorStart(queryDesc, eflags); + else + standard_ExecutorStart(queryDesc, eflags); } /* @@ -323,6 +305,8 @@ qs_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, bool execute_once) #endif { + QueryDescStack = lcons(queryDesc, QueryDescStack); + PG_TRY(); { if (prev_ExecutorRun) @@ -335,10 +319,11 @@ qs_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, else standard_ExecutorRun(queryDesc, direction, count, execute_once); #endif + QueryDescStack = list_delete_first(QueryDescStack); } PG_CATCH(); { - QueryDescStack = NIL; + QueryDescStack = list_delete_first(QueryDescStack); PG_RE_THROW(); } PG_END_TRY(); @@ -351,40 +336,19 @@ qs_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, static void qs_ExecutorFinish(QueryDesc *queryDesc) { + QueryDescStack = lcons(queryDesc, QueryDescStack); + PG_TRY(); { if (prev_ExecutorFinish) prev_ExecutorFinish(queryDesc); else standard_ExecutorFinish(queryDesc); - } - PG_CATCH(); - { - QueryDescStack = NIL; - PG_RE_THROW(); - } - PG_END_TRY(); -} - -/* - * ExecutorEnd hook: - * pop current query description from global stack - */ -static void -qs_ExecutorEnd(QueryDesc *queryDesc) -{ - PG_TRY(); - { QueryDescStack = list_delete_first(QueryDescStack); - - if (prev_ExecutorEnd) - prev_ExecutorEnd(queryDesc); - else - standard_ExecutorEnd(queryDesc); } PG_CATCH(); { - QueryDescStack = NIL; + QueryDescStack = list_delete_first(QueryDescStack); PG_RE_THROW(); } PG_END_TRY(); @@ -640,7 +604,11 @@ pg_query_state(PG_FUNCTION_ARGS) funcctx->max_calls = max_calls; /* Make tuple descriptor */ +#if PG_VERSION_NUM < 120000 tupdesc = CreateTemplateTupleDesc(N_ATTRS, false); +#else + tupdesc = CreateTemplateTupleDesc(N_ATTRS); +#endif TupleDescInitEntry(tupdesc, (AttrNumber) 1, "pid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "frame_number", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "query_text", TEXTOID, -1, 0); @@ -736,8 +704,11 @@ GetRemoteBackendUserId(PGPROC *proc) #if PG_VERSION_NUM < 100000 WaitLatch(MyLatch, WL_LATCH_SET, 0); -#else +#elif PG_VERSION_NUM < 120000 WaitLatch(MyLatch, WL_LATCH_SET, 0, PG_WAIT_EXTENSION); +#else + WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0, + PG_WAIT_EXTENSION); #endif CHECK_FOR_INTERRUPTS(); ResetLatch(MyLatch); @@ -777,9 +748,14 @@ shm_mq_receive_with_timeout(shm_mq_handle *mqh, #if PG_VERSION_NUM < 100000 rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT, delay); +#elif PG_VERSION_NUM < 120000 + rc = WaitLatch(MyLatch, + WL_LATCH_SET | WL_TIMEOUT, + delay, PG_WAIT_EXTENSION); #else - rc = WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT, delay, - PG_WAIT_EXTENSION); + rc = WaitLatch(MyLatch, + WL_LATCH_SET | WL_EXIT_ON_PM_DEATH | WL_TIMEOUT, + delay, PG_WAIT_EXTENSION); #endif INSTR_TIME_SET_CURRENT(cur_time); diff --git a/pg_query_state.h b/pg_query_state.h index 959cd80..a8c13d9 100644 --- a/pg_query_state.h +++ b/pg_query_state.h @@ -14,6 +14,7 @@ #include "commands/explain.h" #include "nodes/pg_list.h" +#include "storage/procarray.h" #include "storage/shm_mq.h" #define QUEUE_SIZE (16 * 1024) diff --git a/signal_handler.c b/signal_handler.c index 0f2cf90..f46d264 100644 --- a/signal_handler.c +++ b/signal_handler.c @@ -166,8 +166,10 @@ SendQueryState(void) #if PG_VERSION_NUM < 100000 WaitLatch(MyLatch, WL_LATCH_SET, 0); -#else +#elif PG_VERSION_NUM < 120000 WaitLatch(MyLatch, WL_LATCH_SET, 0, PG_WAIT_IPC); +#else + WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0, PG_WAIT_IPC); #endif CHECK_FOR_INTERRUPTS(); ResetLatch(MyLatch);