Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uzGWp-001Wfl-0G for pgsql-performance@arkaria.postgresql.org; Thu, 18 Sep 2025 15:26:35 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1uzGWn-00HMLf-Dv for pgsql-performance@arkaria.postgresql.org; Thu, 18 Sep 2025 15:26:33 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uzGWn-00HMLX-3r for pgsql-performance@lists.postgresql.org; Thu, 18 Sep 2025 15:26:33 +0000 Received: from mail1.dalibo.net ([51.159.93.128] helo=mail.dalibo.com) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1uzGWj-001Wpi-09 for pgsql-performance@lists.postgresql.org; Thu, 18 Sep 2025 15:26:32 +0000 Received: from karst (82-65-23-130.subs.proxad.net [82.65.23.130]) by mail.dalibo.com (Postfix) with ESMTPSA id 27767269D0; Thu, 18 Sep 2025 17:26:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=dalibo.com; s=a; t=1758209189; bh=Ud8oR64Y/m15P2esepScb9AygPHDHnPCvpkF6LISHKY=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=cypsM3VHLHfpxxfs1+fzCL7X2GmQn6/yle8gK8DY/yuIU2NZk4VXudJaAOPv49Saw 5vw/z1YL8+Ylg4dXB8XnAy9m9JQGmRn4oGwf0jOYNoyn7zPl52V64RRXi68gCnIw2w WER4Aw28bxIKkAIshXzWcAbE3ESCgrEXTKiT+yFg= Date: Thu, 18 Sep 2025 17:26:28 +0200 From: Jehan-Guillaume de Rorthais To: Tom Lane Cc: =?UTF-8?B?RnLDqWTDqXJpYw==?= Yhuel , "pgsql-performance@lists.postgresql.org" , Christophe Courtois , Laurenz Albe Subject: Re: Indexes on expressions with multiple columns and operators Message-ID: <20250918172628.44301672@karst> In-Reply-To: <1507576.1758120083@sss.pgh.pa.us> References: <1507576.1758120083@sss.pgh.pa.us> Organization: Dalibo MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi there, I think this discussion has a nice solution, thank you! However, while poking around this issue yesterday, we also found something surprising between estimated rows and costs when using a function. Bellow the scenario to apply on top of Frederic's one to quickly expose the weirdness: CREATE OR REPLACE FUNCTION s(crit text, ackid int) RETURNS bool LANGUAGE plpgsql IMMUTABLE AS $$ BEGIN RETURN crit = 'WARNING' AND ackid IS NULL; END $$; CREATE INDEX foo_s_idx ON foo (s(crit, ackid)); ANALYZE foo ; EXPLAIN SELECT * FROM foo WHERE s(crit, ackid); EXPLAIN SELECT * FROM foo WHERE (ackid IS NULL AND crit = 'WARNING') is true; SELECT most_common_vals, most_common_freqs FROM pg_stats WHERE tablename = 'foo_s_idx'; On a fresh instance from HEAD with its default configuration, it shows: Index Scan using foo_s_idx on foo (cost=0.29..8.39 rows=33333 width=13) Index Cond: (s(crit, ackid) = true) Index Scan using foo_expr_idx on foo (cost=0.29..8.39 rows=5 width=13) Index Cond: (((ackid IS NULL) AND (crit = 'WARNING'::text)) = true) most_common_vals | most_common_freqs ------------------+------------------- {f,t} | {0.99995,5e-05} It seems statistics shown in "pg_stats" view for function "s()" are good. The query itself even have the same costs than the query using the syntax tips you provide before. However, the estimated row number seems wrong in regard with the costs shown and statistics. It looks like a default hardcoded 33% has been applied. As Frederic said earlier, this bad row estimation drives the upper join to the wrong method. Is this a known planer behavior? (again, the ju-jitsu syntax provided by Laurenz and you is definitely on point here, I just hijack the thread to discuss this weirdness.) Regards,