Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1gLAVp-0001Jt-Ha for pgsql-hackers@arkaria.postgresql.org; Fri, 09 Nov 2018 17:24:06 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gLAVo-0007on-20 for pgsql-hackers@arkaria.postgresql.org; Fri, 09 Nov 2018 17:24:04 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1gLAVX-0007gO-9U for pgsql-hackers@lists.postgresql.org; Fri, 09 Nov 2018 17:23:47 +0000 Received: from mail.postgrespro.ru ([93.174.131.138]) by magus.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gLAVU-0003fY-1I for pgsql-hackers@postgresql.org; Fri, 09 Nov 2018 17:23:46 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.postgrespro.ru (Postfix) with ESMTP id 9AEBA21C64C5; Fri, 9 Nov 2018 20:23:42 +0300 (MSK) X-Virus-Scanned: Debian amavisd-new at postgrespro.ru X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=x tagged_above=-99 required=4 WHITELISTED tests=[] autolearn=unavailable Received: from [192.168.27.200] (gw.postgrespro.ru [93.174.131.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mail.postgrespro.ru (Postfix) with ESMTPSA id 36BD921C64C3; Fri, 9 Nov 2018 20:23:42 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mail; t=1541784222; bh=zs3nUhqAB0OyD0ZM7tMbQ2IQzCkL4F+uLBoaJYkAbNY=; h=Subject:To:Cc:References:From:Date:In-Reply-To; b=F+RTwDfMTFB+ARlHR8U/X8oJsNOo0SSzR9PFOIaNez35GAt20fggaGkZ6nWIN++qv zAe4HGhp9GJCEYiRX5FDAqlF1/O52rXiB5M72EaNHI+wra2FuWQoIkGQJx/VXar1U/ xc8nWMnvL8L8SzfcF7SWCMNSL/jIHZjUpU9ojIu0= Subject: Re: [HACKERS] Surjective functional indexes To: Alvaro Herrera Cc: Laurenz Albe , Tom Lane , Simon Riggs , Robert Haas , Andres Freund , Stephen Frost , Michael Paquier , Christoph Berg , Thomas Munro , Peter Eisentraut , PostgreSQL Hackers References: <20181108190559.ylzufqr2jjvml32f@alvherre.pgsql> From: Konstantin Knizhnik Message-ID: <1268584c-cd53-386d-c519-0681e92532c9@postgrespro.ru> Date: Fri, 9 Nov 2018 20:23:42 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.2.1 MIME-Version: 1.0 In-Reply-To: <20181108190559.ylzufqr2jjvml32f@alvherre.pgsql> Content-Type: multipart/mixed; boundary="------------1FEBFE9CE4B60E772C2E568E" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------1FEBFE9CE4B60E772C2E568E Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 8bit On 08.11.2018 22:05, Alvaro Herrera wrote: > On 2018-Nov-08, Konstantin Knizhnik wrote: > >> Before doing any other refactoring of projection indexes I want to attach >> small bug fix patch which >> fixes the original problem (SIGSEGV) and also disables recheck_on_update by >> default. >> As Laurenz has suggested, I replaced boolean recheck_on_update option with >> "on","auto,"off" (default). > I think this causes an ABI break for GenericIndexOpts. Not sure to what > extent that is an actual problem (i.e. how many modules were compiled > with 11.0 that are gonna be reading that struct with later Pg), but I > think it should be avoided anyhow. > Ok, I reverted back my change of reach_on_update option type. Now if reach_on_update option is not  explicitly specified, then decision is made based on the expression cost. Patches becomes very small and fix only error in comparison of index tuple values. -- Konstantin Knizhnik Postgres Professional: http://www.postgrespro.com The Russian Postgres Company --------------1FEBFE9CE4B60E772C2E568E Content-Type: text/x-patch; name="projection-11.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="projection-11.patch" diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index fb63471..81ab0ac 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -4546,9 +4546,10 @@ ProjIndexIsUnchanged(Relation relation, HeapTuple oldtup, HeapTuple newtup) } else if (!old_isnull[i]) { - Form_pg_attribute att = TupleDescAttr(RelationGetDescr(indexDesc), i); - - if (!datumIsEqual(old_values[i], new_values[i], att->attbyval, att->attlen)) + int16 elmlen; + bool elmbyval; + get_typlenbyval(indexDesc->rd_opcintype[i], &elmlen, &elmbyval); + if (!datumIsEqual(old_values[i], new_values[i], elmbyval, elmlen)) { equals = false; break; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index fd3d010..ed8a01e 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4745,10 +4745,11 @@ RelationGetIndexPredicate(Relation relation) * for old and new tuple values. * * Decision made by this function is based on two sources: - * 1. Calculated cost of index expression: if greater than some heuristic limit - then extra comparison of index expression values is expected to be too - expensive, so we don't attempt it by default. - * 2. "recheck_on_update" index option explicitly set by user, which overrides 1) + * 1. If "recheck_on_update" is true, then index is considered as projection. + * 2. If "recheck_on_update" is false, then changing any column on which index depends disables hot update + * 3. If "recheck_on_update" is not explicitly specified then recheck on update is + * enabled if cost of index expression is not greater than some heuristic limit (1000). + * In this case extra comparison of index expression values is expected to be too expensive. */ static bool IsProjectionFunctionalIndex(Relation index, IndexInfo *ii) @@ -4760,26 +4761,9 @@ IsProjectionFunctionalIndex(Relation index, IndexInfo *ii) HeapTuple tuple; Datum reloptions; bool isnull; + bool is_explicitly_set = false; QualCost index_expr_cost; - /* by default functional index is considered as non-injective */ - is_projection = true; - - cost_qual_eval(&index_expr_cost, ii->ii_Expressions, NULL); - - /* - * If index expression is too expensive, then disable projection - * optimization, because extra evaluation of index expression is - * expected to be more expensive than index update. Currently the - * projection optimization has to calculate index expression twice - * when the value of index expression has not changed and three times - * when values differ because the expression is recalculated when - * inserting a new index entry for the changed value. - */ - if ((index_expr_cost.startup + index_expr_cost.per_tuple) > - HEURISTIC_MAX_HOT_RECHECK_EXPR_COST) - is_projection = false; - tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(RelationGetRelid(index))); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(index)); @@ -4795,9 +4779,25 @@ IsProjectionFunctionalIndex(Relation index, IndexInfo *ii) if (idxopts != NULL) { is_projection = idxopts->recheck_on_update; + is_explicitly_set = true; pfree(idxopts); } } + if (!is_explicitly_set) + { + cost_qual_eval(&index_expr_cost, ii->ii_Expressions, NULL); + /* + * If index expression is too expensive, then disable projection + * optimization, because extra evaluation of index expression is + * expected to be more expensive than index update. Currently the + * projection optimization has to calculate index expression twice + * when the value of index expression has not changed and three times + * when values differ because the expression is recalculated when + * inserting a new index entry for the changed value. + */ + is_projection = (index_expr_cost.startup + index_expr_cost.per_tuple) + <= HEURISTIC_MAX_HOT_RECHECK_EXPR_COST; + } ReleaseSysCache(tuple); } return is_projection; --------------1FEBFE9CE4B60E772C2E568E--