Re: Hash join explain is broken

2019-08-02 Thread Andres Freund
Hi,

On 2019-07-02 10:50:02 -0400, Tom Lane wrote:
> I wrote:
> > Andres Freund  writes:
> >> Tom, any comments? Otherwise I'll go ahead, and commit after a round or
> >> two of polishing.
> 
> > Sorry for not getting to this sooner --- I'll try to look tomorrow.
> 
> I took a look, and I think this is going in the right direction.
> We definitely need a test case corresponding to the live bug,
> and I think the comments could use more work, and there are some
> cosmetic things (I wouldn't add the new struct Hash field at the
> end, for instance).

I finally pushed a substantially polished version of this. I ended up
moving, as I had wondered about, hashoperator and hashcollation
computation to the planner too - without that we would end up with two
very similar loops during plan and execution time.

I've added a test that puts subplans just about everywhere possible in a
hash join - it's the only reliable way I found to trigger errors (only
during EXPLAIN, as deparsing there tries to find the associated node,
for column names etc, and failed because the subplan referenced an
INNER_VAR, even though Hash doesn't have an inner plan). Makes the test
query a bit hard to read, but I didn't get any better ideas, and it
doesn't seem too bad.

Thanks Tom for the review, thanks Alexander and Nikita for the
report. Sorry that it took this long.

Greetings,

Andres Freund




Re: Hash join explain is broken

2019-07-02 Thread Tom Lane
I wrote:
> Andres Freund  writes:
>> Tom, any comments? Otherwise I'll go ahead, and commit after a round or
>> two of polishing.

> Sorry for not getting to this sooner --- I'll try to look tomorrow.

I took a look, and I think this is going in the right direction.
We definitely need a test case corresponding to the live bug,
and I think the comments could use more work, and there are some
cosmetic things (I wouldn't add the new struct Hash field at the
end, for instance).

regards, tom lane




Re: Hash join explain is broken

2019-07-01 Thread Tom Lane
Andres Freund  writes:
> Tom, any comments? Otherwise I'll go ahead, and commit after a round or
> two of polishing.

Sorry for not getting to this sooner --- I'll try to look tomorrow.

regards, tom lane




Re: Hash join explain is broken

2019-07-01 Thread Andres Freund
Hi,

On 2019-06-18 00:00:28 -0700, Andres Freund wrote:
> On 2019-06-13 16:23:34 -0700, Andres Freund wrote:
> > On June 13, 2019 3:38:47 PM PDT, Tom Lane  wrote:
> > >Andres Freund  writes:
> > >> I am too tired to look further into this. I suspect the only reason
> > >we
> > >> didn't previously run into trouble with the executor stashing
> > >hashkeys
> > >> manually at a different tree level with:
> > >> ((HashState *) innerPlanState(hjstate))->hashkeys
> > >> is that hashkeys itself isn't printed...
> > >
> > >TBH, I think 5f32b29c is just wrong and should be reverted for now.
> > >If there's a need to handle those expressions differently, it will
> > >require some cooperation from the planner not merely a two-line hack
> > >in executor startup.  That commit didn't include any test case or
> > >other demonstration that it was solving a live problem, so I think
> > >we can leave it for v13 to address the issue.
> > 
> > I'm pretty sure you'd get an assertion failure if you reverted it
> > (that's why it was added). So it's a bit more complicated than that.
> > Unfortunately I'll not get back to work until Monday, but I'll spend
> > time on this then.
> 
> Indeed, there are assertion failures when initializing the expression
> with HashJoinState as parent - that's because when computing the
> hashvalue for nodeHash input, we expect the slot from the node below to
> be of the type that HashState returns (as that's what INNER_VAR for an
> expression at the HashJoin level refers to), rather than the type of the
> input to HashState.  We could work around that by marking the slots from
> underlying nodes as being of an unknown type, but that'd slow down
> execution.
> 
> I briefly played with the dirty hack of set_deparse_planstate()
> setting dpns->inner_planstate = ps for IsA(ps, HashState), but that
> seems just too ugly.
> 
> I think the most straight-forward fix might just be to just properly
> split the expression at plan time. Adding workarounds for things as
> dirty as building an expression for a subsidiary node in the parent, and
> then modifying the subsidiary node from the parent, doesn't seem like a
> better way forward.
> 
> The attached *prototype* does so.
> 
> If we go that way, we probably need to:
> - Add a test for the failure case at hand
> - check a few of the comments around inner/outer in nodeHash.c
> - consider moving the setrefs.c code into its own function?
> - probably clean up the naming scheme in createplan.c
> 
> I think there's a few more things we could do, although it's not clear
> that that needs to happen in v12:
> - Consider not extracting hj_OuterHashKeys, hj_HashOperators,
>   hj_Collations out of HashJoin->hashclauses, and instead just directly
>   handing them individually in the planner.  create_mergejoin_plan()
>   already partially does that.

Tom, any comments? Otherwise I'll go ahead, and commit after a round or
two of polishing.

- Andres




Re: Hash join explain is broken

2019-06-18 Thread Andres Freund
Hi,

On 2019-06-13 16:23:34 -0700, Andres Freund wrote:
> On June 13, 2019 3:38:47 PM PDT, Tom Lane  wrote:
> >Andres Freund  writes:
> >> I am too tired to look further into this. I suspect the only reason
> >we
> >> didn't previously run into trouble with the executor stashing
> >hashkeys
> >> manually at a different tree level with:
> >> ((HashState *) innerPlanState(hjstate))->hashkeys
> >> is that hashkeys itself isn't printed...
> >
> >TBH, I think 5f32b29c is just wrong and should be reverted for now.
> >If there's a need to handle those expressions differently, it will
> >require some cooperation from the planner not merely a two-line hack
> >in executor startup.  That commit didn't include any test case or
> >other demonstration that it was solving a live problem, so I think
> >we can leave it for v13 to address the issue.
> 
> I'm pretty sure you'd get an assertion failure if you reverted it
> (that's why it was added). So it's a bit more complicated than that.
> Unfortunately I'll not get back to work until Monday, but I'll spend
> time on this then.

Indeed, there are assertion failures when initializing the expression
with HashJoinState as parent - that's because when computing the
hashvalue for nodeHash input, we expect the slot from the node below to
be of the type that HashState returns (as that's what INNER_VAR for an
expression at the HashJoin level refers to), rather than the type of the
input to HashState.  We could work around that by marking the slots from
underlying nodes as being of an unknown type, but that'd slow down
execution.

I briefly played with the dirty hack of set_deparse_planstate()
setting dpns->inner_planstate = ps for IsA(ps, HashState), but that
seems just too ugly.

I think the most straight-forward fix might just be to just properly
split the expression at plan time. Adding workarounds for things as
dirty as building an expression for a subsidiary node in the parent, and
then modifying the subsidiary node from the parent, doesn't seem like a
better way forward.

The attached *prototype* does so.

If we go that way, we probably need to:
- Add a test for the failure case at hand
- check a few of the comments around inner/outer in nodeHash.c
- consider moving the setrefs.c code into its own function?
- probably clean up the naming scheme in createplan.c

I think there's a few more things we could do, although it's not clear
that that needs to happen in v12:
- Consider not extracting hj_OuterHashKeys, hj_HashOperators,
  hj_Collations out of HashJoin->hashclauses, and instead just directly
  handing them individually in the planner.  create_mergejoin_plan()
  already partially does that.

Greetings,

Andres Freund

PS: If I were to write hashjoin today, it sure wouldn't be as two nodes
- it seems pretty clear that the boundaries are just too fuzzy. To the
point that I wonder if it'd not be worth merging them at some point.
>From ba351470f7d6837a4a86c24ce87ee33919314a77 Mon Sep 17 00:00:00 2001
From: Andres Freund 
Date: Mon, 17 Jun 2019 23:59:38 -0700
Subject: [PATCH v1] wip-fix-hash-key-computation

---
 src/backend/executor/nodeHash.c | 19 +++
 src/backend/executor/nodeHashjoin.c | 18 +++---
 src/backend/nodes/copyfuncs.c   |  1 +
 src/backend/nodes/outfuncs.c|  1 +
 src/backend/nodes/readfuncs.c   |  1 +
 src/backend/optimizer/plan/createplan.c | 13 +
 src/backend/optimizer/plan/setrefs.c| 16 
 src/include/nodes/execnodes.h   |  3 ---
 src/include/nodes/plannodes.h   |  1 +
 9 files changed, 47 insertions(+), 26 deletions(-)

diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index d16120b9c48..181f45a5b1c 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -157,7 +157,8 @@ MultiExecPrivateHash(HashState *node)
 	econtext = node->ps.ps_ExprContext;
 
 	/*
-	 * get all inner tuples and insert into the hash table (or temp files)
+	 * Get all tuples from subsidiary node and insert into the hash table (or
+	 * temp files).
 	 */
 	for (;;)
 	{
@@ -165,7 +166,7 @@ MultiExecPrivateHash(HashState *node)
 		if (TupIsNull(slot))
 			break;
 		/* We have to compute the hash value */
-		econtext->ecxt_innertuple = slot;
+		econtext->ecxt_outertuple = slot;
 		if (ExecHashGetHashValue(hashtable, econtext, hashkeys,
  false, hashtable->keepNulls,
  ))
@@ -281,7 +282,7 @@ MultiExecParallelHash(HashState *node)
 slot = ExecProcNode(outerNode);
 if (TupIsNull(slot))
 	break;
-econtext->ecxt_innertuple = slot;
+econtext->ecxt_outertuple = slot;
 if (ExecHashGetHashValue(hashtable, econtext, hashkeys,
 		 false, hashtable->keepNulls,
 		 ))
@@ -388,8 +389,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	/*
 	 * initialize child expressions
 	 */
-	hashstate->ps.qual =
-		ExecInitQual(node->plan.qual, (PlanState *) hashstate);
+	

Re: Hash join explain is broken

2019-06-13 Thread Andres Freund
Hi,

On June 13, 2019 3:38:47 PM PDT, Tom Lane  wrote:
>Andres Freund  writes:
>> I am too tired to look further into this. I suspect the only reason
>we
>> didn't previously run into trouble with the executor stashing
>hashkeys
>> manually at a different tree level with:
>> ((HashState *) innerPlanState(hjstate))->hashkeys
>> is that hashkeys itself isn't printed...
>
>TBH, I think 5f32b29c is just wrong and should be reverted for now.
>If there's a need to handle those expressions differently, it will
>require some cooperation from the planner not merely a two-line hack
>in executor startup.  That commit didn't include any test case or
>other demonstration that it was solving a live problem, so I think
>we can leave it for v13 to address the issue.

I'm pretty sure you'd get an assertion failure if you reverted it (that's why 
it was added). So it's a bit more complicated than that.  Unfortunately I'll 
not get back to work until Monday, but I'll spend time on this then.

Andres
-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.




Re: Hash join explain is broken

2019-06-13 Thread Tom Lane
Andres Freund  writes:
> I am too tired to look further into this. I suspect the only reason we
> didn't previously run into trouble with the executor stashing hashkeys
> manually at a different tree level with:
> ((HashState *) innerPlanState(hjstate))->hashkeys
> is that hashkeys itself isn't printed...

TBH, I think 5f32b29c is just wrong and should be reverted for now.
If there's a need to handle those expressions differently, it will
require some cooperation from the planner not merely a two-line hack
in executor startup.  That commit didn't include any test case or
other demonstration that it was solving a live problem, so I think
we can leave it for v13 to address the issue.

(But possibly we should add a test case similar to Nikita's,
so that we don't overlook such problems in future.)

regards, tom lane




Re: Hash join explain is broken

2019-06-11 Thread Andres Freund
Hi,

On 2019-06-11 00:45:57 -0700, Andres Freund wrote:
> On 2019-06-10 21:28:12 +0300, Alexander Korotkov wrote:
> > After 5f32b29c explain of Hash Join sometimes triggers an error.
> >
> > Simple reproduction case is below.
> 
> Thanks for finding. I've created an open issue for now.

I am too tired to look further into this. I suspect the only reason we
didn't previously run into trouble with the executor stashing hashkeys
manually at a different tree level with:
((HashState *) innerPlanState(hjstate))->hashkeys
is that hashkeys itself isn't printed...

If done properly, the expression would actually reside in the Hash node
itself, rather than ExecInitHashJoin() splitting up the join condition
itself, and moving it into the HashState.

Greetings,

Andres Freund




Re: Hash join explain is broken

2019-06-11 Thread Andres Freund
Hi,

On 2019-06-10 21:28:12 +0300, Alexander Korotkov wrote:
> After 5f32b29c explain of Hash Join sometimes triggers an error.
>
> Simple reproduction case is below.

Thanks for finding. I've created an open issue for now.

Greetings,

Andres Freund