From: | Kyotaro HORIGUCHI <horiguchi(dot)kyotaro(at)lab(dot)ntt(dot)co(dot)jp> |
---|---|
To: | robertmhaas(at)gmail(dot)com |
Cc: | Langote_Amit_f8(at)lab(dot)ntt(dot)co(dot)jp, pgsql-hackers(at)postgresql(dot)org |
Subject: | Re: default range partition and constraint exclusion |
Date: | 2017-11-27 09:04:57 |
Message-ID: | 20171127.180457.257636417.horiguchi.kyotaro@lab.ntt.co.jp |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
At Fri, 24 Nov 2017 10:49:07 -0500, Robert Haas <robertmhaas(at)gmail(dot)com> wrote in <CA+TgmoarK4aCcSjYheH7QDchb7uJRpiKkGpPo7O9kMBNf13N3w(at)mail(dot)gmail(dot)com>
> On Wed, Nov 22, 2017 at 4:21 AM, Amit Langote
> <Langote_Amit_f8(at)lab(dot)ntt(dot)co(dot)jp> wrote:
> >>> If all predicate_refuted_by() receives is the expression tree (AND/OR)
> >>> with individual nodes being strict clauses involving partition keys (and
> >>> nothing about the nullness of the keys), the downstream code is just
> >>> playing by the rules as explained in the header comment of
> >>> predicate_refuted_by_recurse() in concluding that query's restriction
> >>> clause a = 2 refutes it.
> >>
> >> Oh, wait a minute. Actually, I think predicate_refuted_by() is doing
> >> the right thing here. Isn't the problem that mc2p2 shouldn't be
> >> accepting a (2, null) tuple at all?
> >
> > It doesn't. But, for a query, it does contain (2, <unknown>) tuples,
> > where <unknown> would always be non-null. So, it should be scanned in the
> > plan for the query that has only a = 2 as restriction and no restriction
> > on b. That seems to work.
>
> OK, so I am still confused about whether the constraint is wrong or
> the constraint exclusion logic is wrong. One of them, at least, has
> to be wrong, and we have to fix whichever one is wrong. Fixing broken
> constraint exclusion logic by hacking up the constraint, or conversely
> fixing a broken constraint by hacking up the constraint exclusion
> logic, wouldn't be right.
>
> I think my last email was confused: I thought that the (2, null) tuple
> was ending up in mc2p2, but it's really ending up in mc2p_default,
> whose constraint currently looks like this:
>
> NOT (
> ((a < 1) OR ((a = 1) AND (b < 1)))
> OR
> ((a > 1) OR ((a = 1) AND (b >= 1)))
> )
>
> Now where exactly is constraint exclusion going wrong here? a = 2
> refutes a < 1 and a = 1, which means that (a < 1) OR ((a = 1) AND (b <
> 1)) must be false and that (a = 1) AND (b >= 1) must also be false.
> But (a > 1) could be either true or null, which means (a > 1) OR ((a =
a > 1 is true when a = 2, so the second term is true?
> 1) AND (b >= 1)) can be true or null, which means the whole thing can
> be false or null, which means that it is not refuted by a = 2. It
Then the whole thing is false.
> should be possible to dig down in there step by step and figure out
> where the wheels are coming off -- have you tried to do that?
| select NOT (
| ((a < 1) OR ((a = 1) AND (b < 1)))
| OR
| ((a > 1) OR ((a = 1) AND (b >= 1)))
| )
| from (values (2::int, null::int)) as t(a, b);
| ?column?
| ----------
| f
The problem here I think is that get_qual_for_range() for default
partition returns an inconsistent qual with what partition
get_partition_for_tuple chooses for keys containing nulls. It
chooses default partition if any of the key values is null,
without referring the constraint expression.
The current behavior is apparently odd.
| select pg_get_partition_constraintdef('mc2p2'::regclass);
| pg_get_partition_constraintdef
| ----------------------------------------------------------------------------
| ((a IS NOT NULL) AND (b IS NOT NULL) AND ((a > 1) OR ((a = 1) AND (b >= 1))))
| select pg_get_partition_constraintdef('mc2p_default'::regclass);
| pg_get_partition_constraintdef
|
| ---------------------------------------------------------------------------
| (NOT (((a < 1) OR ((a = 1) AND (b < 1))) OR ((a > 1) OR ((a = 1) AND (b >= 1)))))
| insert into mc2p2 values (2);
| ERROR: new row for relation "mc2p2" violates partition constraint
| DETAIL: Failing row contains (2, null).
This is the correct behavior.
| insert into mc2p_default values (2);
| ERROR: new row for relation "mc2p_default" violates partition constraint
| DETAIL: Failing row contains (2, null).
This is the correct behavior in terms of constraint, but
incorrect in terms of partition routing.
But interestingly, the following *works*, in a way contradicting
to the constraint.
| insert into mc2p values (2);
| INSERT 0 1
|
| select * from mc2p_default;
| a | b
| ---+---
| 2 |
| (1 row)
After applying the patch upthread, get_qual_for_range() returns
the consistent qual and "insert into mc2p_default values (2)" is
accepted correctly and everything become consistent.
This is the story in my understanding.
regards,
--
Kyotaro Horiguchi
NTT Open Source Software Center
From | Date | Subject | |
---|---|---|---|
Next Message | Rui Hai Jiang | 2017-11-27 09:13:09 | RE: How is the PostgreSQL debuginfo file generated |
Previous Message | Amit Kapila | 2017-11-27 09:04:09 | Re: [HACKERS] Challenges preventing us moving to 64 bit transaction id (XID)? |