Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c26506f

Browse files
sokolcatiEkaterina Sokolova
authored and
Ekaterina Sokolova
committedOct 31, 2024·
Merge pull request #56 from postgrespro/PGPRO-11060
Updates for PostgreSQL 17.
2 parents 90399a9 + 1230ab0 commit c26506f

File tree

4 files changed

+506
-1
lines changed

4 files changed

+506
-1
lines changed
 

‎.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ notifications:
2222
on_failure: always
2323

2424
env:
25+
- PG_VERSION=17
2526
- PG_VERSION=16 LEVEL=hardcore USE_TPCDS=1
2627
- PG_VERSION=16
2728
- PG_VERSION=15 LEVEL=hardcore USE_TPCDS=1
@@ -38,6 +39,7 @@ env:
3839

3940
matrix:
4041
allow_failures:
42+
- env: PG_VERSION=13 LEVEL=hardcore USE_TPCDS=1
4143
- env: PG_VERSION=11
4244
- env: PG_VERSION=10
4345
- env: PG_VERSION=9.6

‎patches/custom_signals_17.0.patch

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
2+
index 4ed9ced..6e70892 100644
3+
--- a/src/backend/storage/ipc/procsignal.c
4+
+++ b/src/backend/storage/ipc/procsignal.c
5+
@@ -6,6 +6,7 @@
6+
*
7+
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8+
* Portions Copyright (c) 1994, Regents of the University of California
9+
+ * Portions Copyright (c) 2024, Postgres Professional
10+
*
11+
* IDENTIFICATION
12+
* src/backend/storage/ipc/procsignal.c
13+
@@ -96,6 +97,13 @@ typedef struct
14+
#define BARRIER_CLEAR_BIT(flags, type) \
15+
((flags) &= ~(((uint32) 1) << (uint32) (type)))
16+
17+
+#define IsCustomProcSignalReason(reason) \
18+
+ ((reason) >= PROCSIG_CUSTOM_1 && (reason) <= PROCSIG_CUSTOM_N)
19+
+
20+
+static bool CustomSignalPendings[NUM_CUSTOM_PROCSIGNALS];
21+
+static bool CustomSignalProcessing[NUM_CUSTOM_PROCSIGNALS];
22+
+static ProcSignalHandler_type CustomInterruptHandlers[NUM_CUSTOM_PROCSIGNALS];
23+
+
24+
static ProcSignalHeader *ProcSignal = NULL;
25+
static ProcSignalSlot *MyProcSignalSlot = NULL;
26+
27+
@@ -103,6 +111,8 @@ static bool CheckProcSignal(ProcSignalReason reason);
28+
static void CleanupProcSignalState(int status, Datum arg);
29+
static void ResetProcSignalBarrierBits(uint32 flags);
30+
31+
+static void CheckAndSetCustomSignalInterrupts(void);
32+
+
33+
/*
34+
* ProcSignalShmemSize
35+
* Compute space needed for ProcSignal's shared memory
36+
@@ -242,6 +252,36 @@ CleanupProcSignalState(int status, Datum arg)
37+
slot->pss_pid = 0;
38+
}
39+
40+
+/*
41+
+ * RegisterCustomProcSignalHandler
42+
+ * Assign specific handler of custom process signal with new
43+
+ * ProcSignalReason key.
44+
+ *
45+
+ * This function has to be called in _PG_init function of extensions at the
46+
+ * stage of loading shared preloaded libraries. Otherwise it throws fatal error.
47+
+ *
48+
+ * Return INVALID_PROCSIGNAL if all slots for custom signals are occupied.
49+
+ */
50+
+ProcSignalReason
51+
+RegisterCustomProcSignalHandler(ProcSignalHandler_type handler)
52+
+{
53+
+ ProcSignalReason reason;
54+
+
55+
+ if (!process_shared_preload_libraries_in_progress)
56+
+ ereport(FATAL, (errcode(ERRCODE_INTERNAL_ERROR),
57+
+ errmsg("cannot register custom signal after startup")));
58+
+
59+
+ /* Iterate through custom signal slots to find a free one */
60+
+ for (reason = PROCSIG_CUSTOM_1; reason <= PROCSIG_CUSTOM_N; reason++)
61+
+ if (!CustomInterruptHandlers[reason - PROCSIG_CUSTOM_1])
62+
+ {
63+
+ CustomInterruptHandlers[reason - PROCSIG_CUSTOM_1] = handler;
64+
+ return reason;
65+
+ }
66+
+
67+
+ return INVALID_PROCSIGNAL;
68+
+}
69+
+
70+
/*
71+
* SendProcSignal
72+
* Send a signal to a Postgres process
73+
@@ -676,5 +716,70 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
74+
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
75+
HandleRecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
76+
77+
+ CheckAndSetCustomSignalInterrupts();
78+
+
79+
SetLatch(MyLatch);
80+
}
81+
+
82+
+/*
83+
+ * Handle receipt of an interrupt indicating any of custom process signals.
84+
+ */
85+
+static void
86+
+CheckAndSetCustomSignalInterrupts()
87+
+{
88+
+ ProcSignalReason reason;
89+
+
90+
+ for (reason = PROCSIG_CUSTOM_1; reason <= PROCSIG_CUSTOM_N; reason++)
91+
+ {
92+
+ if (CheckProcSignal(reason))
93+
+ {
94+
+
95+
+ /* set interrupt flags */
96+
+ InterruptPending = true;
97+
+ CustomSignalPendings[reason - PROCSIG_CUSTOM_1] = true;
98+
+ }
99+
+ }
100+
+
101+
+ SetLatch(MyLatch);
102+
+}
103+
+
104+
+/*
105+
+ * CheckAndHandleCustomSignals
106+
+ * Check custom signal flags and call handler assigned to that signal
107+
+ * if it is not NULL
108+
+ *
109+
+ * This function is called within CHECK_FOR_INTERRUPTS if interrupt occurred.
110+
+ */
111+
+void
112+
+CheckAndHandleCustomSignals(void)
113+
+{
114+
+ int i;
115+
+
116+
+ /*
117+
+ * This is invoked from ProcessInterrupts(), and since some of the
118+
+ * functions it calls contain CHECK_FOR_INTERRUPTS(), there is a potential
119+
+ * for recursive calls if more signals are received while this runs, so
120+
+ * let's block interrupts until done.
121+
+ */
122+
+ HOLD_INTERRUPTS();
123+
+
124+
+ /* Check on expiring of custom signals and call its handlers if exist */
125+
+ for (i = 0; i < NUM_CUSTOM_PROCSIGNALS; i++)
126+
+ {
127+
+ if (!CustomSignalProcessing[i] && CustomSignalPendings[i])
128+
+ {
129+
+ ProcSignalHandler_type handler;
130+
+
131+
+ CustomSignalPendings[i] = false;
132+
+ handler = CustomInterruptHandlers[i];
133+
+ if (handler != NULL)
134+
+ {
135+
+ CustomSignalProcessing[i] = true;
136+
+ handler();
137+
+ CustomSignalProcessing[i] = false;
138+
+ }
139+
+ }
140+
+ }
141+
+
142+
+ RESUME_INTERRUPTS();
143+
+}
144+
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
145+
index a750dc8..e1b0be5 100644
146+
--- a/src/backend/tcop/postgres.c
147+
+++ b/src/backend/tcop/postgres.c
148+
@@ -3492,6 +3492,8 @@ ProcessInterrupts(void)
149+
if (ParallelMessagePending)
150+
HandleParallelMessages();
151+
152+
+ CheckAndHandleCustomSignals();
153+
+
154+
if (LogMemoryContextPending)
155+
ProcessLogMemoryContextInterrupt();
156+
157+
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
158+
index 7d290ea..f262f0c 100644
159+
--- a/src/include/storage/procsignal.h
160+
+++ b/src/include/storage/procsignal.h
161+
@@ -6,6 +6,7 @@
162+
*
163+
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
164+
* Portions Copyright (c) 1994, Regents of the University of California
165+
+ * Portions Copyright (c) 2024, Postgres Professional
166+
*
167+
* src/include/storage/procsignal.h
168+
*
169+
@@ -17,6 +18,8 @@
170+
#include "storage/procnumber.h"
171+
172+
173+
+#define NUM_CUSTOM_PROCSIGNALS 64
174+
+
175+
/*
176+
* Reasons for signaling a Postgres child process (a backend or an auxiliary
177+
* process, like checkpointer). We can cope with concurrent signals for different
178+
@@ -29,6 +32,8 @@
179+
*/
180+
typedef enum
181+
{
182+
+ INVALID_PROCSIGNAL = -1, /* Must be first */
183+
+
184+
PROCSIG_CATCHUP_INTERRUPT, /* sinval catchup interrupt */
185+
PROCSIG_NOTIFY_INTERRUPT, /* listen/notify interrupt */
186+
PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */
187+
@@ -37,6 +42,14 @@ typedef enum
188+
PROCSIG_LOG_MEMORY_CONTEXT, /* ask backend to log the memory contexts */
189+
PROCSIG_PARALLEL_APPLY_MESSAGE, /* Message from parallel apply workers */
190+
191+
+ PROCSIG_CUSTOM_1,
192+
+ /*
193+
+ * PROCSIG_CUSTOM_2,
194+
+ * ...,
195+
+ * PROCSIG_CUSTOM_N-1,
196+
+ */
197+
+ PROCSIG_CUSTOM_N = PROCSIG_CUSTOM_1 + NUM_CUSTOM_PROCSIGNALS - 1,
198+
+
199+
/* Recovery conflict reasons */
200+
PROCSIG_RECOVERY_CONFLICT_FIRST,
201+
PROCSIG_RECOVERY_CONFLICT_DATABASE = PROCSIG_RECOVERY_CONFLICT_FIRST,
202+
@@ -56,6 +69,9 @@ typedef enum
203+
PROCSIGNAL_BARRIER_SMGRRELEASE, /* ask smgr to close files */
204+
} ProcSignalBarrierType;
205+
206+
+/* Handler of custom process signal */
207+
+typedef void (*ProcSignalHandler_type) (void);
208+
+
209+
/*
210+
* prototypes for functions in procsignal.c
211+
*/
212+
@@ -63,12 +79,15 @@ extern Size ProcSignalShmemSize(void);
213+
extern void ProcSignalShmemInit(void);
214+
215+
extern void ProcSignalInit(void);
216+
+extern ProcSignalReason
217+
+ RegisterCustomProcSignalHandler(ProcSignalHandler_type handler);
218+
extern int SendProcSignal(pid_t pid, ProcSignalReason reason,
219+
ProcNumber procNumber);
220+
221+
extern uint64 EmitProcSignalBarrier(ProcSignalBarrierType type);
222+
extern void WaitForProcSignalBarrier(uint64 generation);
223+
extern void ProcessProcSignalBarrier(void);
224+
+extern void CheckAndHandleCustomSignals(void);
225+
226+
extern void procsignal_sigusr1_handler(SIGNAL_ARGS);
227+

‎patches/runtime_explain_17.0.patch

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
2+
index 18a5af6b919..73d3d6171eb 100644
3+
--- a/src/backend/commands/explain.c
4+
+++ b/src/backend/commands/explain.c
5+
@@ -18,6 +18,7 @@
6+
#include "commands/createas.h"
7+
#include "commands/defrem.h"
8+
#include "commands/prepare.h"
9+
+#include "executor/nodeHash.h"
10+
#include "foreign/fdwapi.h"
11+
#include "jit/jit.h"
12+
#include "libpq/pqformat.h"
13+
@@ -1233,14 +1234,36 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
14+
char *relname;
15+
char *conname = NULL;
16+
17+
+ instr_time starttimespan;
18+
+ double total;
19+
+ double ntuples;
20+
+ double ncalls;
21+
+
22+
+ if (!es->runtime)
23+
+ {
24+
/* Must clean up instrumentation state */
25+
InstrEndLoop(instr);
26+
+ }
27+
+
28+
+ /* Collect statistic variables */
29+
+ if (!INSTR_TIME_IS_ZERO(instr->starttime))
30+
+ {
31+
+ INSTR_TIME_SET_CURRENT(starttimespan);
32+
+ INSTR_TIME_SUBTRACT(starttimespan, instr->starttime);
33+
+ }
34+
+ else
35+
+ INSTR_TIME_SET_ZERO(starttimespan);
36+
+
37+
+ total = instr->total + INSTR_TIME_GET_DOUBLE(instr->counter)
38+
+ + INSTR_TIME_GET_DOUBLE(starttimespan);
39+
+ ntuples = instr->ntuples + instr->tuplecount;
40+
+ ncalls = ntuples + !INSTR_TIME_IS_ZERO(starttimespan);
41+
42+
/*
43+
* We ignore triggers that were never invoked; they likely aren't
44+
* relevant to the current query type.
45+
*/
46+
- if (instr->ntuples == 0)
47+
+ if (ncalls == 0)
48+
continue;
49+
50+
ExplainOpenGroup("Trigger", NULL, true, es);
51+
@@ -1266,9 +1289,9 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
52+
appendStringInfo(es->str, " on %s", relname);
53+
if (es->timing)
54+
appendStringInfo(es->str, ": time=%.3f calls=%.0f\n",
55+
- 1000.0 * instr->total, instr->ntuples);
56+
+ 1000.0 * total, ncalls);
57+
else
58+
- appendStringInfo(es->str, ": calls=%.0f\n", instr->ntuples);
59+
+ appendStringInfo(es->str, ": calls=%.0f\n", ncalls);
60+
}
61+
else
62+
{
63+
@@ -1277,9 +1300,8 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
64+
ExplainPropertyText("Constraint Name", conname, es);
65+
ExplainPropertyText("Relation", relname, es);
66+
if (es->timing)
67+
- ExplainPropertyFloat("Time", "ms", 1000.0 * instr->total, 3,
68+
- es);
69+
- ExplainPropertyFloat("Calls", NULL, instr->ntuples, 0, es);
70+
+ ExplainPropertyFloat("Time", "ms", 1000.0 * total, 3, es);
71+
+ ExplainPropertyFloat("Calls", NULL, ncalls, 0, es);
72+
}
73+
74+
if (conname)
75+
@@ -1949,8 +1971,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
76+
* instrumentation results the user didn't ask for. But we do the
77+
* InstrEndLoop call anyway, if possible, to reduce the number of cases
78+
* auto_explain has to contend with.
79+
+ *
80+
+ * If flag es->stateinfo is set, i.e. when printing the current execution
81+
+ * state, this step of cleaning up is missed.
82+
*/
83+
- if (planstate->instrument)
84+
+ if (planstate->instrument && !es->runtime)
85+
InstrEndLoop(planstate->instrument);
86+
87+
if (es->analyze &&
88+
@@ -1985,7 +2010,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
89+
ExplainPropertyFloat("Actual Loops", NULL, nloops, 0, es);
90+
}
91+
}
92+
- else if (es->analyze)
93+
+ else if (es->analyze && !es->runtime)
94+
{
95+
if (es->format == EXPLAIN_FORMAT_TEXT)
96+
appendStringInfoString(es->str, " (never executed)");
97+
@@ -2001,6 +2026,75 @@ ExplainNode(PlanState *planstate, List *ancestors,
98+
}
99+
}
100+
101+
+ /*
102+
+ * Print the progress of node execution at current loop.
103+
+ */
104+
+ if (planstate->instrument && es->analyze && es->runtime)
105+
+ {
106+
+ instr_time starttimespan;
107+
+ double startup_sec;
108+
+ double total_sec;
109+
+ double rows;
110+
+ double loop_num;
111+
+ bool finished;
112+
+
113+
+ if (!INSTR_TIME_IS_ZERO(planstate->instrument->starttime))
114+
+ {
115+
+ INSTR_TIME_SET_CURRENT(starttimespan);
116+
+ INSTR_TIME_SUBTRACT(starttimespan, planstate->instrument->starttime);
117+
+ }
118+
+ else
119+
+ INSTR_TIME_SET_ZERO(starttimespan);
120+
+ startup_sec = 1000.0 * planstate->instrument->firsttuple;
121+
+ total_sec = 1000.0 * (INSTR_TIME_GET_DOUBLE(planstate->instrument->counter)
122+
+ + INSTR_TIME_GET_DOUBLE(starttimespan));
123+
+ rows = planstate->instrument->tuplecount;
124+
+ loop_num = planstate->instrument->nloops + 1;
125+
+
126+
+ finished = planstate->instrument->nloops > 0
127+
+ && !planstate->instrument->running
128+
+ && INSTR_TIME_IS_ZERO(starttimespan);
129+
+
130+
+ if (!finished)
131+
+ {
132+
+ ExplainOpenGroup("Current loop", "Current loop", true, es);
133+
+ if (es->format == EXPLAIN_FORMAT_TEXT)
134+
+ {
135+
+ if (es->timing)
136+
+ {
137+
+ if (planstate->instrument->running)
138+
+ appendStringInfo(es->str,
139+
+ " (Current loop: actual time=%.3f..%.3f rows=%.0f, loop number=%.0f)",
140+
+ startup_sec, total_sec, rows, loop_num);
141+
+ else
142+
+ appendStringInfo(es->str,
143+
+ " (Current loop: running time=%.3f actual rows=0, loop number=%.0f)",
144+
+ total_sec, loop_num);
145+
+ }
146+
+ else
147+
+ appendStringInfo(es->str,
148+
+ " (Current loop: actual rows=%.0f, loop number=%.0f)",
149+
+ rows, loop_num);
150+
+ }
151+
+ else
152+
+ {
153+
+ ExplainPropertyFloat("Actual Loop Number", NULL, loop_num, 0, es);
154+
+ if (es->timing)
155+
+ {
156+
+ if (planstate->instrument->running)
157+
+ {
158+
+ ExplainPropertyFloat("Actual Startup Time", NULL, startup_sec, 3, es);
159+
+ ExplainPropertyFloat("Actual Total Time", NULL, total_sec, 3, es);
160+
+ }
161+
+ else
162+
+ ExplainPropertyFloat("Running Time", NULL, total_sec, 3, es);
163+
+ }
164+
+ ExplainPropertyFloat("Actual Rows", NULL, rows, 0, es);
165+
+ }
166+
+ ExplainCloseGroup("Current loop", "Current loop", true, es);
167+
+ }
168+
+ }
169+
+
170+
/* in text format, first line ends here */
171+
if (es->format == EXPLAIN_FORMAT_TEXT)
172+
appendStringInfoChar(es->str, '\n');
173+
@@ -2416,6 +2510,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
174+
175+
/* Prepare per-worker buffer/WAL usage */
176+
if (es->workers_state && (es->buffers || es->wal) && es->verbose)
177+
+ /* Show worker detail after query execution */
178+
+ if (es->analyze && es->verbose && planstate->worker_instrument
179+
+ && !es->runtime)
180+
{
181+
WorkerInstrumentation *w = planstate->worker_instrument;
182+
183+
@@ -3403,6 +3500,11 @@ show_hash_info(HashState *hashstate, ExplainState *es)
184+
memcpy(&hinstrument, hashstate->hinstrument,
185+
sizeof(HashInstrumentation));
186+
187+
+ if (hashstate->hashtable)
188+
+ {
189+
+ ExecHashAccumInstrumentation(&hinstrument, hashstate->hashtable);
190+
+ }
191+
+
192+
/*
193+
* Merge results from workers. In the parallel-oblivious case, the
194+
* results from all participants should be identical, except where
195+
@@ -3937,20 +4039,16 @@ show_instrumentation_count(const char *qlabel, int which,
196+
if (!es->analyze || !planstate->instrument)
197+
return;
198+
199+
+ nloops = planstate->instrument->nloops;
200+
if (which == 2)
201+
- nfiltered = planstate->instrument->nfiltered2;
202+
+ nfiltered = ((nloops > 0) ? planstate->instrument->nfiltered2 / nloops : 0);
203+
else
204+
- nfiltered = planstate->instrument->nfiltered1;
205+
+ nfiltered = ((nloops > 0) ? planstate->instrument->nfiltered1 / nloops : 0);
206+
nloops = planstate->instrument->nloops;
207+
208+
/* In text mode, suppress zero counts; they're not interesting enough */
209+
if (nfiltered > 0 || es->format != EXPLAIN_FORMAT_TEXT)
210+
- {
211+
- if (nloops > 0)
212+
- ExplainPropertyFloat(qlabel, NULL, nfiltered / nloops, 0, es);
213+
- else
214+
- ExplainPropertyFloat(qlabel, NULL, 0.0, 0, es);
215+
- }
216+
+ ExplainPropertyFloat(qlabel, NULL, nfiltered, 0, es);
217+
}
218+
219+
/*
220+
@@ -4617,15 +4715,27 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
221+
double insert_path;
222+
double other_path;
223+
224+
- InstrEndLoop(outerPlanState(mtstate)->instrument);
225+
+ if (!es->runtime)
226+
+ InstrEndLoop(outerPlanState(mtstate)->instrument);
227+
228+
/* count the number of source rows */
229+
- total = outerPlanState(mtstate)->instrument->ntuples;
230+
other_path = mtstate->ps.instrument->ntuples2;
231+
- insert_path = total - other_path;
232+
233+
- ExplainPropertyFloat("Tuples Inserted", NULL,
234+
- insert_path, 0, es);
235+
+ /*
236+
+ * Insert occurs after extracting row from subplan and in runtime mode
237+
+ * we can appear between these two operations - situation when
238+
+ * total > insert_path + other_path. Therefore we don't know exactly
239+
+ * whether last row from subplan is inserted.
240+
+ * We don't print inserted tuples in runtime mode in order to not print
241+
+ * inconsistent data
242+
+ */
243+
+ if (!es->runtime)
244+
+ {
245+
+ total = outerPlanState(mtstate)->instrument->ntuples;
246+
+ insert_path = total - other_path;
247+
+ ExplainPropertyFloat("Tuples Inserted", NULL, insert_path, 0, es);
248+
+ }
249+
+
250+
ExplainPropertyFloat("Conflicting Tuples", NULL,
251+
other_path, 0, es);
252+
}
253+
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
254+
index 3ab0aae78f7..3644c0db116 100644
255+
--- a/src/include/commands/explain.h
256+
+++ b/src/include/commands/explain.h
257+
@@ -57,6 +57,8 @@ typedef struct ExplainState
258+
bool generic; /* generate a generic plan */
259+
ExplainSerializeOption serialize; /* serialize the query's output? */
260+
ExplainFormat format; /* output format */
261+
+ bool runtime; /* print intermediate state of query execution,
262+
+ not after completion */
263+
/* state for output formatting --- not reset for each new plan tree */
264+
int indent; /* current indentation level */
265+
List *grouping_stack; /* format-specific grouping state */

‎tests/test_cases.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ def test_nested_call(config):
110110
expected = 'Function Scan on n_join_foo_bar (Current loop: actual rows=0, loop number=1)'
111111
expected_nested = r"""Result \(Current loop: actual rows=0, loop number=1\)
112112
InitPlan 1 \(returns \$0\)
113+
-> Aggregate \(Current loop: actual rows=0, loop number=1\)
114+
-> Hash Join \(Current loop: actual rows=\d+, loop number=1\)
115+
Hash Cond: \(foo.c1 = bar.c1\)
116+
Join Filter: \(unlock_if_eq_1\(foo.c1\) = bar.c1\)
117+
-> Seq Scan on foo \(Current loop: actual rows=\d+, loop number=1\)
118+
-> Hash \(Current loop: actual rows=500000, loop number=1\)
119+
Buckets: \d+ Batches: \d+ Memory Usage: \d+kB
120+
-> Seq Scan on bar \(Current loop: actual rows=\d+, loop number=1\)"""
121+
122+
expected_nested_2 = r"""Result \(Current loop: actual rows=0, loop number=1\)
123+
InitPlan 1
113124
-> Aggregate \(Current loop: actual rows=0, loop number=1\)
114125
-> Hash Join \(Current loop: actual rows=\d+, loop number=1\)
115126
Hash Cond: \(foo.c1 = bar.c1\)
@@ -136,7 +147,7 @@ def test_nested_call(config):
136147
assert qs[0][2] == call_function
137148
assert qs[0][3] == expected
138149
assert qs[1][2] == nested_query1 or qs[1][2] == nested_query2
139-
assert re.match(expected_nested, qs[1][3])
150+
assert re.match(expected_nested, qs[1][3]) or re.match(expected_nested_2, qs[1][3])
140151
assert qs[0][4] == qs[1][4] == None
141152
assert len(notices) == 0
142153

0 commit comments

Comments
 (0)
Please sign in to comment.