Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qHPOr-0000OT-Ps for pgsql-hackers@arkaria.postgresql.org; Thu, 06 Jul 2023 13:52:02 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1qHPOq-0003wT-G2 for pgsql-hackers@arkaria.postgresql.org; Thu, 06 Jul 2023 13:52:00 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qHPOp-0003wK-Rd for pgsql-hackers@lists.postgresql.org; Thu, 06 Jul 2023 13:52:00 +0000 Received: from forward501c.mail.yandex.net ([2a02:6b8:c03:500:1:45:d181:d501]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1qHPOk-002Mai-R1 for pgsql-hackers@lists.postgresql.org; Thu, 06 Jul 2023 13:51:58 +0000 Received: from mail-nwsmtp-smtp-production-main-25.sas.yp-c.yandex.net (mail-nwsmtp-smtp-production-main-25.sas.yp-c.yandex.net [IPv6:2a02:6b8:c08:2e14:0:640:2cd1:0]) by forward501c.mail.yandex.net (Yandex) with ESMTP id BDCD55F022; Thu, 6 Jul 2023 16:51:49 +0300 (MSK) Received: by mail-nwsmtp-smtp-production-main-25.sas.yp-c.yandex.net (smtp/Yandex) with ESMTPSA id mpU5xNEDbeA0-yVybdAVV; Thu, 06 Jul 2023 16:51:49 +0300 X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex.ru; s=mail; t=1688651509; bh=sOW6OT9/f3DpmzJhRZrkyw/hfjNubUPc7w6latnmCys=; h=In-Reply-To:Cc:Date:References:To:Subject:Message-ID:From; b=v78bfFN8RVyKs9IkUI6m974DBufsE+bEP6uYXgh4TpcB38g2v6U1dd32qpinS9H8t vuy4f+9b98cR5zagsXlW5gGd+iRy1Dm1HzlYVPKJMUMvDJ8MVhsRdBENwddH4ehvAg w+KIL7Rpf01KiFokqTd2vx53oX9WL2djlX7B8oUg= Authentication-Results: mail-nwsmtp-smtp-production-main-25.sas.yp-c.yandex.net; dkim=pass header.i=@yandex.ru Content-Type: multipart/mixed; boundary="------------3EjEgJepGEGdLyyagPwR6cQu" Message-ID: <148ff8f1-067b-1409-c754-af6117de9b7d@yandex.ru> Date: Thu, 6 Jul 2023 16:51:48 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: Problems with estimating OR conditions, IS NULL on LEFT JOINs To: Tomas Vondra Cc: PostgreSQL Hackers , Andrey Lepikhov , Tom Lane References: <52b55b53-420a-722c-adbd-706922fc059b@enterprisedb.com> <104950.1687565314@sss.pgh.pa.us> <6dce55f4-e8f6-8e8e-b2af-b45ccff13598@enterprisedb.com> <6ed3a29e-b4b9-e4b6-e8a1-b1d7d00bbd88@postgrespro.ru> <7af1464e-2e24-cfb1-b6d4-1544757f8cfa@yandex.ru> <63baadec-933b-53f8-5653-99f490dce09e@enterprisedb.com> Content-Language: en-US From: Alena Rybakina In-Reply-To: <63baadec-933b-53f8-5653-99f490dce09e@enterprisedb.com> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk This is a multi-part message in MIME format. --------------3EjEgJepGEGdLyyagPwR6cQu Content-Type: multipart/alternative; boundary="------------HG0VsmoM37UHbWNOdmw3fnK0" --------------HG0VsmoM37UHbWNOdmw3fnK0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hi, all! On 26.06.2023 12:22, Andrey Lepikhov wrote: > On 24/6/2023 17:23, Tomas Vondra wrote: >> I really hope what I just wrote makes at least a little bit of sense. > Throw in one more example: > > SELECT i AS id INTO l FROM generate_series(1,100000) i; > CREATE TABLE r (id int8, v text); > INSERT INTO r (id, v) VALUES (1, 't'), (-1, 'f'); > ANALYZE l,r; > EXPLAIN ANALYZE > SELECT * FROM l LEFT OUTER JOIN r ON (r.id = l.id) WHERE r.v IS NULL; > > Here you can see the same kind of underestimation: > Hash Left Join  (... rows=500 width=14) (... rows=99999 ...) > > So the eqjoinsel_unmatch_left() function should be modified for the > case where nd1 > > Unfortunately, this patch could not fix the cardinality calculation in > this request, I'll try to look and figure out what is missing here. I tried to fix the cardinality score in the query above by changing: diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 8e18aa1dd2b..40901836146 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -2604,11 +2604,16 @@ eqjoinsel_inner(Oid opfuncoid, Oid collation,                  * if we're calculating fraction of NULLs or fraction of unmatched rows.                  */                 // unmatchfreq = (1.0 - nullfrac1) * (1.0 - nullfrac2); -               if (nd1 > nd2) +               if (nd1 != nd2)                 { -                       selec /= nd1; -                       *unmatched_frac = (nd1 - nd2) * 1.0 / nd1; +                       selec /= Max(nd1, nd2); +                       *unmatched_frac = abs(nd1 - nd2) * 1.0 / Max(nd1, nd2);                 } +               /*if (nd1 > nd2) +               { +                       selec /= nd1; +                       *unmatched_frac = nd1 - nd2 * 1.0 / nd1; +               }*/                 else                 {                         selec /= nd2; and it worked: SELECT i AS id INTO l FROM generate_series(1,100000) i; CREATE TABLE r (id int8, v text); INSERT INTO r (id, v) VALUES (1, 't'), (-1, 'f'); ANALYZE l,r; EXPLAIN ANALYZE SELECT * FROM l LEFT OUTER JOIN r ON (r.id = l.id) WHERE r.v IS NULL; ERROR:  relation "l" already exists ERROR:  relation "r" already exists INSERT 0 2 ANALYZE                                                   QUERY PLAN ---------------------------------------------------------------------------------------------------------------  Hash Left Join  (cost=1.09..1944.13 rows=99998 width=14) (actual time=0.152..84.184 rows=99999 loops=1)    Hash Cond: (l.id = r.id)    Filter: (r.v IS NULL)    Rows Removed by Filter: 2    ->  Seq Scan on l  (cost=0.00..1443.00 rows=100000 width=4) (actual time=0.040..27.635 rows=100000 loops=1)    ->  Hash  (cost=1.04..1.04 rows=4 width=10) (actual time=0.020..0.022 rows=4 loops=1)          Buckets: 1024  Batches: 1  Memory Usage: 9kB          ->  Seq Scan on r  (cost=0.00..1.04 rows=4 width=10) (actual time=0.009..0.011 rows=4 loops=1)  Planning Time: 0.954 ms  Execution Time: 92.309 ms (10 rows) It looks too simple and I suspect that I might have missed something somewhere, but so far I haven't found any examples of queries where it doesn't work. I didn't see it breaking anything in the examples from my previous letter [1]. 1. https://www.postgresql.org/message-id/7af1464e-2e24-cfb1-b6d4-1544757f8cfa%40yandex.ru Unfortunately, I can't understand your idea from point 4, please explain it? The good thing is this helps even for IS NULL checks on non-join-key columns (where we don't switch to an antijoin), but there's a couple things that I dislike ... 1) It's not restricted to outer joins or anything like that (this is mostly just my laziness / interest in one particular query, but also something the outer-join-aware patch might help with). 2) We probably don't want to pass this kind of information through sjinfo. That was the simplest thing for an experimental patch, but I suspect it's not the only piece of information we may need to pass to the lower levels of estimation code. 3) I kinda doubt we actually want to move this responsibility (to consider fraction of unmatched rows) to the low-level estimation routines (e.g. nulltestsel and various others). AFAICS this just "introduces NULLs" into the relation, so maybe we could "adjust" the attribute statistics (in examine_variable?) by inflating null_frac and modifying the other frequencies in MCV/histogram. 4) But I'm not sure we actually want to do that in these low-level selectivity functions. The outer join essentially produces output with two subsets - one with matches on the outer side, one without them. But the side without matches has NULLs in all columns. In a way, we know exactly how are these columns correlated - if we do the usual estimation (even with the null_frac adjusted), we just throw this information away. And when there's a lot of rows without a match, that seems bad. -- Regards, Alena Rybakina Postgres Professional --------------HG0VsmoM37UHbWNOdmw3fnK0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit

Hi, all!

On 26.06.2023 12:22, Andrey Lepikhov wrote:
On 24/6/2023 17:23, Tomas Vondra wrote:
I really hope what I just wrote makes at least a little bit of sense.
Throw in one more example:

SELECT i AS id INTO l FROM generate_series(1,100000) i;
CREATE TABLE r (id int8, v text);
INSERT INTO r (id, v) VALUES (1, 't'), (-1, 'f');
ANALYZE l,r;
EXPLAIN ANALYZE
SELECT * FROM l LEFT OUTER JOIN r ON (r.id = l.id) WHERE r.v IS NULL;

Here you can see the same kind of underestimation:
Hash Left Join  (... rows=500 width=14) (... rows=99999 ...)

So the eqjoinsel_unmatch_left() function should be modified for the case where nd1<nd2.


Unfortunately, this patch could not fix the cardinality calculation in this request, I'll try to look and figure out what is missing here.

I tried to fix the cardinality score in the query above by changing:

diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 8e18aa1dd2b..40901836146 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -2604,11 +2604,16 @@ eqjoinsel_inner(Oid opfuncoid, Oid collation,
                 * if we're calculating fraction of NULLs or fraction of unmatched rows.
                 */
                // unmatchfreq = (1.0 - nullfrac1) * (1.0 - nullfrac2);
-               if (nd1 > nd2)
+               if (nd1 != nd2)
                {
-                       selec /= nd1;
-                       *unmatched_frac = (nd1 - nd2) * 1.0 / nd1;
+                       selec /= Max(nd1, nd2);
+                       *unmatched_frac = abs(nd1 - nd2) * 1.0 / Max(nd1, nd2);
                }
+               /*if (nd1 > nd2)
+               {
+                       selec /= nd1;
+                       *unmatched_frac = nd1 - nd2 * 1.0 / nd1;
+               }*/
                else
                {
                        selec /= nd2;

and it worked:

SELECT i AS id INTO l FROM generate_series(1,100000) i;
CREATE TABLE r (id int8, v text);
INSERT INTO r (id, v) VALUES (1, 't'), (-1, 'f');
ANALYZE l,r;
EXPLAIN ANALYZE
SELECT * FROM l LEFT OUTER JOIN r ON (r.id = l.id) WHERE r.v IS NULL;
ERROR:  relation "l" already exists
ERROR:  relation "r" already exists
INSERT 0 2
ANALYZE
                                                  QUERY PLAN                                                   
---------------------------------------------------------------------------------------------------------------
 Hash Left Join  (cost=1.09..1944.13 rows=99998 width=14) (actual time=0.152..84.184 rows=99999 loops=1)
   Hash Cond: (l.id = r.id)
   Filter: (r.v IS NULL)
   Rows Removed by Filter: 2
   ->  Seq Scan on l  (cost=0.00..1443.00 rows=100000 width=4) (actual time=0.040..27.635 rows=100000 loops=1)
   ->  Hash  (cost=1.04..1.04 rows=4 width=10) (actual time=0.020..0.022 rows=4 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on r  (cost=0.00..1.04 rows=4 width=10) (actual time=0.009..0.011 rows=4 loops=1)
 Planning Time: 0.954 ms
 Execution Time: 92.309 ms
(10 rows)

It looks too simple and I suspect that I might have missed something somewhere, but so far I haven't found any examples of queries where it doesn't work.

I didn't see it breaking anything in the examples from my previous letter [1].

1. https://www.postgresql.org/message-id/7af1464e-2e24-cfb1-b6d4-1544757f8cfa%40yandex.ru


Unfortunately, I can't understand your idea from point 4, please explain it?

The good thing is this helps even for IS NULL checks on non-join-key
columns (where we don't switch to an antijoin), but there's a couple
things that I dislike ...

1) It's not restricted to outer joins or anything like that (this is
mostly just my laziness / interest in one particular query, but also
something the outer-join-aware patch might help with).

2) We probably don't want to pass this kind of information through
sjinfo. That was the simplest thing for an experimental patch, but I
suspect it's not the only piece of information we may need to pass to
the lower levels of estimation code.

3) I kinda doubt we actually want to move this responsibility (to
consider fraction of unmatched rows) to the low-level estimation
routines (e.g. nulltestsel and various others). AFAICS this just
"introduces NULLs" into the relation, so maybe we could "adjust" the
attribute statistics (in examine_variable?) by inflating null_frac and
modifying the other frequencies in MCV/histogram.

4) But I'm not sure we actually want to do that in these low-level
selectivity functions. The outer join essentially produces output with
two subsets - one with matches on the outer side, one without them. But
the side without matches has NULLs in all columns. In a way, we know
exactly how are these columns correlated - if we do the usual estimation
(even with the null_frac adjusted), we just throw this information away.
And when there's a lot of rows without a match, that seems bad.

-- 
Regards,
Alena Rybakina
Postgres Professional
--------------HG0VsmoM37UHbWNOdmw3fnK0-- --------------3EjEgJepGEGdLyyagPwR6cQu Content-Type: text/x-patch; charset=UTF-8; name="0001-Fixed-the-case-of-calculating-underestimated-cardina.patch" Content-Disposition: attachment; filename*0="0001-Fixed-the-case-of-calculating-underestimated-cardina.pa"; filename*1="tch" Content-Transfer-Encoding: base64 RnJvbSA1NGEyNDM5MGYxMTM3YTc3Yzk3NTVhODc1Nzc0YTc1YWU4Y2YyNDI0IE1vbiBTZXAg MTcgMDA6MDA6MDAgMjAwMQpGcm9tOiBUb21hcyBWb25kcmEgPHRvbWFzLnZvbmRyYUBlbnRl cnByaXNlZGIuY29tPgpEYXRlOiBNb24sIDI2IEp1biAyMDIzIDE1OjM5OjExICswMzAwClN1 YmplY3Q6IFtQQVRDSF0gRml4ZWQgdGhlIGNhc2Ugb2YgY2FsY3VsYXRpbmcgdW5kZXJlc3Rp bWF0ZWQgY2FyZGluYWxpdHkgZm9yCiBhbiBMRUZUIEpPSU4gd2l0aCB0aGUgcmVzdHJpY3Rp b24gIklTIE5VTEwiIGluIHRoZSBjbGF1c2UuIFRoaXMgZXJyb3IgaXMKIGNhdXNlZCBieSBh biBpbmNvcnJlY3QgY2FsY3VsYXRpb24gb2Ygc2VsZWN0aXZpdHkgaW4gdGhlICJJUyBOVUxM IiBjbGF1c2UsCiBzaW5jZSBpdCB0b29rIGludG8gYWNjb3VudCBvbmx5IHRhYmxlLWxldmVs IHN0YXRpc3RpY3Mgd2l0aG91dCB6ZXJvIHZhbHVlcyBpbgogdGhlIHJlc3VsdHMgb2YgdGhl IGpvaW4gb3BlcmF0aW9uLiBUaGlzIHBhdGNoIGZpeGVzIHRoaXMgYnkgY2FsY3VsYXRpbmcg dGhlCiBmcmFjdGlvbiBvZiB6ZXJvIHZhbHVlcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUg am9pbiB3aXRob3V0IG9mIG1hdGNoaW5nIHJvdwogb24gbGVmdC4KCkNvLWF1dGhvcmVkLWJ5 OiBBbGVuYSBSeWJha2luYSA8YS5yeWJha2luYUBwb3N0Z3Jlc3Byby5ydT4KLS0tCiBzcmMv YmFja2VuZC91dGlscy9hZHQvc2VsZnVuY3MuYyB8IDM5ICsrKysrKysrKysrKysrKysrKysr KysrKysrKystLS0tCiBzcmMvaW5jbHVkZS9ub2Rlcy9wYXRobm9kZXMuaCAgICB8ICAzICsr KwogMiBmaWxlcyBjaGFuZ2VkLCAzNyBpbnNlcnRpb25zKCspLCA1IGRlbGV0aW9ucygtKQoK ZGlmZiAtLWdpdCBhL3NyYy9iYWNrZW5kL3V0aWxzL2FkdC9zZWxmdW5jcy5jIGIvc3JjL2Jh Y2tlbmQvdXRpbHMvYWR0L3NlbGZ1bmNzLmMKaW5kZXggYzRmY2QwMDc2ZWEuLmEwZTM4MzQ0 NTNiIDEwMDY0NAotLS0gYS9zcmMvYmFja2VuZC91dGlscy9hZHQvc2VsZnVuY3MuYworKysg Yi9zcmMvYmFja2VuZC91dGlscy9hZHQvc2VsZnVuY3MuYwpAQCAtMTUzLDcgKzE1Myw3IEBA IHN0YXRpYyBkb3VibGUgZXFqb2luc2VsX2lubmVyKE9pZCBvcGZ1bmNvaWQsIE9pZCBjb2xs YXRpb24sCiAJCQkJCQkJICBib29sIGlzZGVmYXVsdDEsIGJvb2wgaXNkZWZhdWx0MiwKIAkJ CQkJCQkgIEF0dFN0YXRzU2xvdCAqc3Nsb3QxLCBBdHRTdGF0c1Nsb3QgKnNzbG90MiwKIAkJ CQkJCQkgIEZvcm1fcGdfc3RhdGlzdGljIHN0YXRzMSwgRm9ybV9wZ19zdGF0aXN0aWMgc3Rh dHMyLAotCQkJCQkJCSAgYm9vbCBoYXZlX21jdnMxLCBib29sIGhhdmVfbWN2czIpOworCQkJ CQkJCSAgYm9vbCBoYXZlX21jdnMxLCBib29sIGhhdmVfbWN2czIsIGRvdWJsZSAqdW5tYXRj aGVkX2ZyYWMpOwogc3RhdGljIGRvdWJsZSBlcWpvaW5zZWxfc2VtaShPaWQgb3BmdW5jb2lk LCBPaWQgY29sbGF0aW9uLAogCQkJCQkJCSBWYXJpYWJsZVN0YXREYXRhICp2YXJkYXRhMSwg VmFyaWFibGVTdGF0RGF0YSAqdmFyZGF0YTIsCiAJCQkJCQkJIGRvdWJsZSBuZDEsIGRvdWJs ZSBuZDIsCkBAIC0xNzEwLDYgKzE3MTAsOSBAQCBudWxsdGVzdHNlbChQbGFubmVySW5mbyAq cm9vdCwgTnVsbFRlc3RUeXBlIG51bGx0ZXN0dHlwZSwgTm9kZSAqYXJnLAogCQlzdGF0cyA9 IChGb3JtX3BnX3N0YXRpc3RpYykgR0VUU1RSVUNUKHZhcmRhdGEuc3RhdHNUdXBsZSk7CiAJ CWZyZXFfbnVsbCA9IHN0YXRzLT5zdGFudWxsZnJhYzsKIAorCQlpZiAoc2ppbmZvKQorCQkJ ZnJlcV9udWxsID0gZnJlcV9udWxsICsgc2ppbmZvLT51bm1hdGNoZWRfZnJhYyAtIGZyZXFf bnVsbCAqIHNqaW5mby0+dW5tYXRjaGVkX2ZyYWM7CisKIAkJc3dpdGNoIChudWxsdGVzdHR5 cGUpCiAJCXsKIAkJCWNhc2UgSVNfTlVMTDoKQEAgLTIzMTMsMTMgKzIzMTYsMjQgQEAgZXFq b2luc2VsKFBHX0ZVTkNUSU9OX0FSR1MpCiAJfQogCiAJLyogV2UgbmVlZCB0byBjb21wdXRl IHRoZSBpbm5lci1qb2luIHNlbGVjdGl2aXR5IGluIGFsbCBjYXNlcyAqLworCS8qCisJICog Y2FsY3VsYXRlIGZyYWN0aW9uIG9mIHJpZ2h0IHdpdGhvdXQgb2YgbWF0Y2hpbmcgcm93IG9u IGxlZnQKKwkgKgorCSAqIEZJWE1FIFNob3VsZCBiZSByZXN0cmljdGVkIHRvIEpPSU5fTEVG VCwgd2Ugc2hvdWxkIGhhdmUgc2ltaWxhciBsb2dpYworCSAqIGZvciBKT0lOX0ZVTEwuCisJ ICoKKwkgKiBYWFggUHJvYmFibHkgc2hvdWxkIGNhbGN1bGF0ZSB1bm1hdGNoZWQgYXMgZnJh Y3Rpb24gb2YgdGhlIGpvaW4gcmVzdWx0LAorCSAqIG5vdCBvZiB0aGUgcmVsYXRpb24gb24g dGhlIHJpZ2h0IChiZWNhdXNlIHRoZSBtYXRjaGVkIHBhcnQgY2FuIGhhdmUgbW9yZQorCSAq IG1hdGNoZXMgcGVyIHJvdyBhbmQgdGh1cyBncm93KS4gTm90IHN1cmUuIERlcGVuZHMgb24g aG93IGl0J3MgdXNlZCBsYXRlci4KKwkgKi8KIAlzZWxlY19pbm5lciA9IGVxam9pbnNlbF9p bm5lcihvcGZ1bmNvaWQsIGNvbGxhdGlvbiwKIAkJCQkJCQkJICAmdmFyZGF0YTEsICZ2YXJk YXRhMiwKIAkJCQkJCQkJICBuZDEsIG5kMiwKIAkJCQkJCQkJICBpc2RlZmF1bHQxLCBpc2Rl ZmF1bHQyLAogCQkJCQkJCQkgICZzc2xvdDEsICZzc2xvdDIsCiAJCQkJCQkJCSAgc3RhdHMx LCBzdGF0czIsCi0JCQkJCQkJCSAgaGF2ZV9tY3ZzMSwgaGF2ZV9tY3ZzMik7CisJCQkJCQkJ CSAgaGF2ZV9tY3ZzMSwgaGF2ZV9tY3ZzMiwKKwkJCQkJCQkJICAmc2ppbmZvLT51bm1hdGNo ZWRfZnJhYyk7CiAKIAlzd2l0Y2ggKHNqaW5mby0+am9pbnR5cGUpCiAJewpAQCAtMjQwNyw3 ICsyNDIxLDcgQEAgZXFqb2luc2VsX2lubmVyKE9pZCBvcGZ1bmNvaWQsIE9pZCBjb2xsYXRp b24sCiAJCQkJYm9vbCBpc2RlZmF1bHQxLCBib29sIGlzZGVmYXVsdDIsCiAJCQkJQXR0U3Rh dHNTbG90ICpzc2xvdDEsIEF0dFN0YXRzU2xvdCAqc3Nsb3QyLAogCQkJCUZvcm1fcGdfc3Rh dGlzdGljIHN0YXRzMSwgRm9ybV9wZ19zdGF0aXN0aWMgc3RhdHMyLAotCQkJCWJvb2wgaGF2 ZV9tY3ZzMSwgYm9vbCBoYXZlX21jdnMyKQorCQkJCWJvb2wgaGF2ZV9tY3ZzMSwgYm9vbCBo YXZlX21jdnMyLCBkb3VibGUgKnVubWF0Y2hlZF9mcmFjKQogewogCWRvdWJsZQkJc2VsZWM7 CiAKQEAgLTI1MDMsNyArMjUxNywxMCBAQCBlcWpvaW5zZWxfaW5uZXIoT2lkIG9wZnVuY29p ZCwgT2lkIGNvbGxhdGlvbiwKIAkJfQogCQlDTEFNUF9QUk9CQUJJTElUWShtYXRjaGZyZXEx KTsKIAkJQ0xBTVBfUFJPQkFCSUxJVFkodW5tYXRjaGZyZXExKTsKKworCQkqdW5tYXRjaGVk X2ZyYWMgPSB1bm1hdGNoZnJlcTE7CiAJCW1hdGNoZnJlcTIgPSB1bm1hdGNoZnJlcTIgPSAw LjA7CisKIAkJZm9yIChpID0gMDsgaSA8IHNzbG90Mi0+bnZhbHVlczsgaSsrKQogCQl7CiAJ CQlpZiAoaGFzbWF0Y2gyW2ldKQpAQCAtMjU4MSwxMCArMjU5OCwyMiBAQCBlcWpvaW5zZWxf aW5uZXIoT2lkIG9wZnVuY29pZCwgT2lkIGNvbGxhdGlvbiwKIAkJZG91YmxlCQludWxsZnJh YzIgPSBzdGF0czIgPyBzdGF0czItPnN0YW51bGxmcmFjIDogMC4wOwogCiAJCXNlbGVjID0g KDEuMCAtIG51bGxmcmFjMSkgKiAoMS4wIC0gbnVsbGZyYWMyKTsKLQkJaWYgKG5kMSA+IG5k MikKLQkJCXNlbGVjIC89IG5kMTsKKworCQkvKgorCQkgKiBYWFggU2hvdWxkIHRoaXMgbG9v ayBhdCBudWxsZnJhYyBvbiBlaXRoZXIgc2lkZT8gUHJvYmFibHkgZGVwZW5kcyBvbgorCQkg KiBpZiB3ZSdyZSBjYWxjdWxhdGluZyBmcmFjdGlvbiBvZiBOVUxMcyBvciBmcmFjdGlvbiBv ZiB1bm1hdGNoZWQgcm93cy4KKwkJICovCisJCS8vIHVubWF0Y2hmcmVxID0gKDEuMCAtIG51 bGxmcmFjMSkgKiAoMS4wIC0gbnVsbGZyYWMyKTsKKwkJaWYgKG5kMSAhPSBuZDIpCisJCXsK KwkJCXNlbGVjIC89IE1heChuZDEsIG5kMik7CisJCQkqdW5tYXRjaGVkX2ZyYWMgPSBhYnMo bmQxIC0gbmQyKSAqIDEuMCAvIE1heChuZDEsIG5kMik7CisJCX0KIAkJZWxzZQorCQl7CiAJ CQlzZWxlYyAvPSBuZDI7CisJCQkqdW5tYXRjaGVkX2ZyYWMgPSAwLjA7CisJCX0KIAl9CiAK IAlyZXR1cm4gc2VsZWM7CmRpZmYgLS1naXQgYS9zcmMvaW5jbHVkZS9ub2Rlcy9wYXRobm9k ZXMuaCBiL3NyYy9pbmNsdWRlL25vZGVzL3BhdGhub2Rlcy5oCmluZGV4IGMxN2I1M2Y3YWRi Li42YmM2M2U2NDhlNiAxMDA2NDQKLS0tIGEvc3JjL2luY2x1ZGUvbm9kZXMvcGF0aG5vZGVz LmgKKysrIGIvc3JjL2luY2x1ZGUvbm9kZXMvcGF0aG5vZGVzLmgKQEAgLTI4NTMsNiArMjg1 Myw5IEBAIHN0cnVjdCBTcGVjaWFsSm9pbkluZm8KIAlib29sCQlzZW1pX2Nhbl9oYXNoOwkv KiB0cnVlIGlmIHNlbWlfb3BlcmF0b3JzIGFyZSBhbGwgaGFzaCAqLwogCUxpc3QJICAgKnNl bWlfb3BlcmF0b3JzOyAvKiBPSURzIG9mIGVxdWFsaXR5IGpvaW4gb3BlcmF0b3JzICovCiAJ TGlzdAkgICAqc2VtaV9yaHNfZXhwcnM7IC8qIHJpZ2h0aGFuZC1zaWRlIGV4cHJlc3Npb25z IG9mIHRoZXNlIG9wcyAqLworCisJLyogRm9yIG91dGVyIGpvaW4sIGZyYWN0aW9uIG9mIHJv d3Mgd2l0aG91dCBhIG1hdGNoLiAqLworCVNlbGVjdGl2aXR5CXVubWF0Y2hlZF9mcmFjOwog fTsKIAogLyoKLS0gCjIuMzQuMQoK --------------3EjEgJepGEGdLyyagPwR6cQu--