Re: Foreign join pushdown vs EvalPlanQual

From: Etsuro Fujita <fujita(dot)etsuro(at)lab(dot)ntt(dot)co(dot)jp>
To: Kouhei Kaigai <kaigai(at)ak(dot)jp(dot)nec(dot)com>, Robert Haas <robertmhaas(at)gmail(dot)com>
Cc: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Kyotaro HORIGUCHI <horiguchi(dot)kyotaro(at)lab(dot)ntt(dot)co(dot)jp>, "pgsql-hackers(at)postgresql(dot)org" <pgsql-hackers(at)postgresql(dot)org>, Shigeru Hanada <shigeru(dot)hanada(at)gmail(dot)com>
Subject: Re: Foreign join pushdown vs EvalPlanQual
Date: 2015-11-26 12:59:52
Message-ID: 56570248.7030306@lab.ntt.co.jp
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 2015/11/26 14:04, Kouhei Kaigai wrote:
>> On 2015/11/24 2:41, Robert Haas wrote:
>>> On Fri, Nov 20, 2015 at 12:11 AM, Kouhei Kaigai <kaigai(at)ak(dot)jp(dot)nec(dot)com> wrote:
>>>> One subplan means FDW driver run an entire join sub-tree with local
>>>> alternative sub-plan; that is my expectation for the majority case.

>>> What I'm imagining is that we'd add handling that allows the
>>> ForeignScan to have inner and outer children. If the FDW wants to
>>> delegate the EvalPlanQual handling to a local plan, it can use the
>>> outer child for that. Or the inner one, if it likes. The other one
>>> is available for some other purposes which we can't imagine yet. If
>>> this is too weird, we can only add handling for an outer subplan and
>>> forget about having an inner subplan for now. I just thought to make
>>> it symmetric, since outer and inner subplans are pretty deeply baked
>>> into the structure of the system.

>> I'd vote for only allowing an outer subplan.

> The attached patch adds: Path *fdw_outerpath field to ForeignPath node.
> FDW driver can set arbitrary but one path-node here.
> After that, this path-node shall be transformed to plan-node by
> createplan.c, then passed to FDW driver using GetForeignPlan callback.

I understand this, as I also did the same thing in my patches, but
actually, that seems a bit complicated to me. Instead, could we keep
the fdw_outerpath in the fdw_private of a ForeignPath node when creating
the path node during GetForeignPaths, and then create an outerplan
accordingly from the fdw_outerpath stored into the fdw_private during
GetForeignPlan, by using create_plan_recurse there? I think that that
would make the core involvment much simpler.

> We expect FDW driver set this plan-node on lefttree (a.k.a outerPlan).
> The Plan->outerPlan is a common field, so patch size become relatively
> small. FDW driver can initialize this plan at BeginForeignScan, then
> execute this sub-plan-tree on demand.

Another idea would be to add the core support for
initializing/closing/rescanning the outerplan tree when the tree is given.

> Remaining portions are as previous version. ExecScanFetch is revised
> to call recheckMtd always when scanrelid==0, then FDW driver can get
> control using RecheckForeignScan callback.
> It allows FDW driver to handle (1) EPQ recheck on underlying scan nodes,
> (2) reconstruction of joined tuple, and (3) EPQ recheck on join clauses,
> by its preferable implementation - including execution of an alternative
> sub-plan.

@@ -85,6 +86,18 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot
*slot)

ResetExprContext(econtext);

+ /*
+ * FDW driver has to recheck visibility of EPQ tuple towards
+ * the scan qualifiers once it gets pushed down.
+ * In addition, if this node represents a join sub-tree, not
+ * a scan, FDW driver is also responsible to reconstruct
+ * a joined tuple according to the primitive EPQ tuples.
+ */
+ if (fdwroutine->RecheckForeignScan)
+ {
+ if (!fdwroutine->RecheckForeignScan(node, slot))
+ return false;
+ }

Maybe I'm missing something, but I think we should let FDW do the work
if scanrelid==0, not just if fdwroutine->RecheckForeignScan is given.
(And if scanrelid==0 and fdwroutine->RecheckForeignScan is not given, we
should abort the transaction.)

Another thing I'm concerned about is

@@ -347,8 +355,26 @@ ExecScanReScan(ScanState *node)
{
Index scanrelid = ((Scan *) node->ps.plan)->scanrelid;

- Assert(scanrelid > 0);
+ if (scanrelid > 0)
+ estate->es_epqScanDone[scanrelid - 1] = false;
+ else
+ {
+ Bitmapset *relids;
+ int rtindex = -1;
+
+ if (IsA(node->ps.plan, ForeignScan))
+ relids = ((ForeignScan *) node->ps.plan)->fs_relids;
+ else if (IsA(node->ps.plan, CustomScan))
+ relids = ((CustomScan *) node->ps.plan)->custom_relids;
+ else
+ elog(ERROR, "unexpected scan node: %d",
+ (int)nodeTag(node->ps.plan));

- estate->es_epqScanDone[scanrelid - 1] = false;
+ while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
+ {
+ Assert(rtindex > 0);
+ estate->es_epqScanDone[rtindex - 1] = false;
+ }
+ }
}

That seems the outerplan's business to me, so I think it'd be better to
just return, right before the assertion, as I said before. Seen from
another angle, ISTM that FDWs that don't use a local join execution plan
wouldn't need to be aware of handling the es_epqScanDone flags. (Do you
think that such FDWs should do something like what ExecScanFtch is doing
about the flags, in their RecheckForeignScans? If so, I think we need
docs for that.)

>> There seems to be no changes to make_foreignscan. Is that OK?

> create_foreignscan_path(), not only make_foreignscan().

OK

> This patch is not tested by actual FDW extensions, so it is helpful
> to enhance postgres_fdw to run the alternative sub-plan on EPQ recheck.

Will do.

Best regards,
Etsuro Fujita

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Magnus Hagander 2015-11-26 13:53:13 Re: pg_stat_replication log positions vs base backups
Previous Message Rushabh Lathia 2015-11-26 12:09:19 Re: Getting sorted data from foreign server for merge join