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.96) (envelope-from ) id 1vhDaP-00BP37-0M for pgsql-performance@arkaria.postgresql.org; Sat, 17 Jan 2026 21:11:57 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vhDZO-009Zm5-2G for pgsql-performance@arkaria.postgresql.org; Sat, 17 Jan 2026 21:10:55 +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.96) (envelope-from ) id 1vhDZO-009Zlx-1F for pgsql-performance@lists.postgresql.org; Sat, 17 Jan 2026 21:10:54 +0000 Received: from sss.pgh.pa.us ([68.162.161.243]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vhDZM-0013E9-17 for pgsql-performance@lists.postgresql.org; Sat, 17 Jan 2026 21:10:54 +0000 Received: from sss1.sss.pgh.pa.us (localhost [127.0.0.1]) by sss.pgh.pa.us (8.15.2/8.15.2) with ESMTP id 60HLAjl8077031; Sat, 17 Jan 2026 16:10:45 -0500 From: Tom Lane To: Andrei Lepikhov cc: Lillian Berry , pgsql-performance@lists.postgresql.org, infra@pluralkit.me Subject: Re: Slow queries on simple index In-reply-to: <1d79d974-acfe-483c-81da-61989ed2f91b@gmail.com> References: <1d79d974-acfe-483c-81da-61989ed2f91b@gmail.com> Comments: In-reply-to Andrei Lepikhov message dated "Sat, 17 Jan 2026 21:21:36 +0100" MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <77029.1768684245.1@sss.pgh.pa.us> Content-Transfer-Encoding: quoted-printable Date: Sat, 17 Jan 2026 16:10:45 -0500 Message-ID: <77030.1768684245@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Andrei Lepikhov writes: > Hmm, where is the evidence that your query uses theindex? Maybe the = > generic plan accidentally forces SeqScan? Oh ... you're on to something. I think we all assumed that this was an intermittent problem, but if it happens every time, I bet it's a datatype mismatch issue. Observe: postgres=3D# create table members(hid char(6) unique not null); CREATE TABLE postgres=3D# prepare p as select * from members where hid =3D '42'; PREPARE postgres=3D# explain execute p; QUERY PLAN = = --------------------------------------------------------------------------= ----------- Index Only Scan using members_hid_key on members (cost=3D0.15..8.17 rows= =3D1 width=3D10) Index Cond: (hid =3D '42'::bpchar) (2 rows) postgres=3D# prepare p2(char) as select * from members where hid =3D $1; PREPARE postgres=3D# explain execute p2('42'); QUERY PLAN = = --------------------------------------------------------------------------= ----------- Index Only Scan using members_hid_key on members (cost=3D0.15..8.17 rows= =3D1 width=3D10) Index Cond: (hid =3D '42'::bpchar) (2 rows) postgres=3D# prepare p3(text) as select * from members where hid =3D $1; PREPARE postgres=3D# explain execute p3('42'); QUERY PLAN = ---------------------------------------------------------- Seq Scan on members (cost=3D0.00..42.10 rows=3D11 width=3D10) Filter: ((hid)::text =3D '42'::text) (2 rows) If the $1 parameter is declared to be type "text" then it wins the tug-of-war over which equals operator is used, and text-equals-text does not match the bpchar index. If it's not convenient to alter whatever aspect of the client logic is causing that parameter to be marked as text, you could force the issue by putting a cast into the text of the statement: postgres=3D# prepare p4(text) as select * from members where hid =3D $1::c= har(6); PREPARE postgres=3D# explain execute p4('42'); QUERY PLAN = = --------------------------------------------------------------------------= ----------- Index Only Scan using members_hid_key on members (cost=3D0.15..8.17 rows= =3D1 width=3D10) Index Cond: (hid =3D '42 '::character(6)) (2 rows) Or better yet, avoid using char(n). It's a legacy type with no real reason to exist, and it has semantic gotchas beyond this one. varchar(n) is a much better idea. regards, tom lane