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 1w16BM-002YfB-0y for pgsql-general@arkaria.postgresql.org; Fri, 13 Mar 2026 17:20:16 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w16BK-005TE7-23 for pgsql-general@arkaria.postgresql.org; Fri, 13 Mar 2026 17:20:15 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w11KM-003kNb-0x for pgsql-general@lists.postgresql.org; Fri, 13 Mar 2026 12:09:14 +0000 Received: from mail-ej1-x635.google.com ([2a00:1450:4864:20::635]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w11KK-00000001vz2-2xRh for pgsql-general@postgresql.org; Fri, 13 Mar 2026 12:09:13 +0000 Received: by mail-ej1-x635.google.com with SMTP id a640c23a62f3a-b976e181895so87594266b.1 for ; Fri, 13 Mar 2026 05:09:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1773403751; cv=none; d=google.com; s=arc-20240605; b=VdUX/uF7jqW25n8WOQAvZeA4SwV625+TO2Em/5DWDNWyxT3ru/Xs1gfxGY1oyp2mac FS/A/VZxhCwQDpqdykXEQ2FCOy8bEqr4/79JSHrfZAIlPOKCHfOA6WJcWQ/L/3ce+dTH TVKuCjqD+mA16EiGiGMiXhHYqAaW2rA9QMKh1gwkNkyODozM1dGd55KaZ5pgysoJ+T4q UVW3f57Pv9n4CHtYNYgAFLEAowlGZauY3oe/083jMhQ+oB5rZLZVLb7kyr0NDoGxL+Yj EG0J8LqrSPljflvNwyEdwHr3Y7w4nZGWKLpxjm9VvnaFvBRC2SShyWYvNtpUdRtLnN22 av3A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=to:subject:message-id:date:from:mime-version:dkim-signature; bh=ui2gLm1GXpB1bqnsYv6z4A1ceUdkAXKFcy1OPGxJApc=; fh=x9Jmr8+FtiCQKUvo/7/je6SkcsG+V48pI/LmEXQOSec=; b=WNrzqAF3TEOcs348hB1MrNj9s/uJpsaH7/Hlnww/RPxI9KE4At+ncJ0c2l7Vmwx0d7 oJLpfGp09yT//4jG1IzEh5wpozLJmmZfHDgW+Yeu6NjGipbte9UGUaUTG/DX5Nv3EKtT VYmKzPwpeB+DzY1leBi7VkQ3Fg3BeBfeelFM98Oui+zbAIOc+B5qfBf3achUGa/GQF/d 8kYchIhZ9sQ4UgQnFl5dPIo6fdk3uZ1js45ltbzxc/BGcszYJSfDcT/R9aaHxUyleTt4 ikVm09wsTMpGK7MPzvAUjNBgx+14oi56Ylj3GvqeixerSDOLvR9Zryh6bXi8+9Q75i4I LGfQ==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20230601; t=1773403751; x=1774008551; darn=postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=ui2gLm1GXpB1bqnsYv6z4A1ceUdkAXKFcy1OPGxJApc=; b=KvWHHRcI704bImGXAn6enEcOUs6QjvWO+VZMjJzXtZpU6IBsHJuhpV+4ZKb7+/wbAT i/d8wex/smx2dNqKHb9EMNO3IFiuOqfXb7gtclwNJriuvyAiC/jVz7K4yNPlXfc/zPzP FL7ThA3IvrqPKWw/NqIrbi/uke36nA40Y9X1/enX0ZBctX+OL4hQAFVTjKZ5rkHdyowS wVO+2jZcOLrhsgX1tk4qAkoxaHIFgCb6SnOjS/GDTZoXvGPvj7NTLZhKJYm3egZ06IiE oQ87c4qDf0Wti1vAM8/cC2RVvbcAh8c5c7nrnwcr65tjcsONPlC7EDXnCj6B7QbbBdF6 MWHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1773403751; x=1774008551; h=to:subject:message-id:date:from:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ui2gLm1GXpB1bqnsYv6z4A1ceUdkAXKFcy1OPGxJApc=; b=EIGQMP295b9pCiGvLccxVhM2/FehDNrOTp8OOc0ypZIIVelk2Mg4UXNzzEohwA/rRU qTY9hDJsQ0YRVvgn3Et3Vo1Dz3D+0DSqKVyWSoeKJ3TjLeFf8uAiMyT8dn299iP2udap O9vXFDqONLRKcrdpcpNkunNT/lIH0piAaH1iJXKHvPGKOmGwndAMMNQXDylKbsIphl8K XSrozW/uZMHLBwgYKRh52ZFX9WV9FQ8l/jYzKnJ0sGoWU1OhVXP8Oh4vpleH58nxxBjB qRWqxykXt6CHo3wceP6exuzxJj3gB54cvqzlBlqKiPxybciXbDODeZxG6LFghBdAiU+0 ERSQ== X-Gm-Message-State: AOJu0YyTjuvw+3soV/LTU9iJ8H8sxsN/mEduRj8F0dGOz+R9bgQPc9bL BLSlewAmbpsaNkLPjhJRfmu07wW05B+zyGh/it+mPSGplxiV5xonHh9oLbt8g5OTIHj6MvSDiHz XCHQ5Cx65QjoxEyg5eqDoSju5/lSFxBYOTZRs X-Gm-Gg: ATEYQzxrP9rJHkBY3KE7t2qd1lFv5ORszgslvrUNQWwKxIrBNsJVtBrT6iwVSQt9Y+B cr8xGJ2ijT2NlaUYlWb4BFT5XWus8qavEdQPNOx++lMZQWXzkZr8jrcb0+5VG6aa0A9RZJI4eHn gwRXMX2XB1Dvfc0nXq3d1ZWY4JNlczQ9Nkj8bOH4udT+hbCqSBsnGiXNSePFRsRle1wdTOgOk+G S0PNW8JApBQYgcC+N42yM+jxJp/ZJWHgsfrVUl6t9NYObK3Jdbcwv1DyJbNmeZwNt+DZdG3bU7D vLkEY2Y= X-Received: by 2002:a17:907:a42:b0:b97:17b7:d8eb with SMTP id a640c23a62f3a-b9765392026mr177731566b.38.1773403750653; Fri, 13 Mar 2026 05:09:10 -0700 (PDT) MIME-Version: 1.0 From: Laurence Newman Date: Fri, 13 Mar 2026 12:08:59 +0000 X-Gm-Features: AaiRm514iIBk2rWF-hzocrvEXosYo9hg0pzJw1V7YQ5PbJGFhWE194L0u4Gu-7A Message-ID: Subject: Index scan with bitmap filter - has this been explored To: pgsql-general@postgresql.org Content-Type: multipart/alternative; boundary="00000000000099e170064ce6bc57" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000099e170064ce6bc57 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hello, I've been thinking about a scan strategy that is sort of in between a bitmap scan and an index scan, and I wanted to check whether this has been explored before much or if there's a reason it wouldn't work well in practice. Example scenario where it could be useful: you need the first N rows ordered by a B-tree column, filtered by a condition on another column with a GIN index, e.g. full text search. Today I believe Postgres has two options that use indexes: 1. Bitmap scan on the filter index =E2=86=92 heap fetch all matches =E2= =86=92 sort =E2=86=92 limit. Can be expensive when there are many matches but you only want a few results returned. 2. Index scan on the B-tree =E2=86=92 recheck the filter condition agai= nst the heap for each row. Can be expensive when the match rate is low and many non-matching rows are found before finding N hits. The idea for the new scan strategy is: build the TID bitmap from the filter index first (same as a bitmap scan would), then walk the B-tree in order, but instead of going to the heap to check the filter condition, check each TID against the bitmap. Matching TIDs get returned directly in order. I built a small proof of concept as a pgrx extension against PG 18 to try and test this: https://github.com/lauri3new/ordered-bitmap-scan. The benchmarks there show it using fewer buffer hits than either a bitmap scan or index scan for the example data distribution - which is quite skewed to show the benefit. I want to caveat that I am a web backend developer and not a Postgres expert by any means, so there's probably good reasons this isn't a thing (planner cost or benefit is too small/niche). I would appreciate though any pointers to prior discussion or feedback on whether this is worth looking into further or not. Thanks --00000000000099e170064ce6bc57 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello,

I've been thinking about a scan strategy= that is sort of in between a bitmap scan and an=C2=A0 index=C2=A0scan, and= I wanted to check whether this has been explored before much or if there&#= 39;s a
reason it wouldn't work well in practice.

Example scen= ario where it=C2=A0could be useful: you need the first N rows ordered by a = B-tree column, filtered by a condition on another column with a GIN index, = e.g. full text search. Today I believe Postgres has two options that use in= dexes:

=C2=A0 =C2=A0 1. Bitmap scan on the filter index =E2=86=92 he= ap fetch all matches =E2=86=92 sort =E2=86=92 limit. Can be expensive
= =C2=A0when there are many matches but you only want a few results returned.=
=C2=A0 =C2=A0 2. Index scan on the B-tree =E2=86=92 recheck the filter = condition against the heap for each
=C2=A0row. Can be expensive when the= match rate is low and many non-matching rows are found before=C2=A0finding= N hits.

The idea for the new scan strategy is: build the TID bitmap= from the filter index first (same as a bitmap scan would), then walk the B= -tree in order, but instead of going to the heap to check the
filter con= dition, check each TID against the bitmap. Matching TIDs get returned
di= rectly in order.

I built a small proof of concept as a pgrx extensio= n against PG 18 to try and test this:
https://github.com/lauri3new/ordered-bitmap-scan= . The benchmarks there show it using fewer buffer hits than either a bi= tmap scan or index scan for the example data distribution - which is quite = skewed to show the benefit.

I want to caveat that I am a web backend= developer and not a Postgres expert by any means, so there's=C2=A0prob= ably good reasons this=C2=A0isn't a thing (planner cost or benefit is t= oo small/niche). I would appreciate though any pointers to prior discussion= or feedback on whether this is worth looking into further or not.

T= hanks
--00000000000099e170064ce6bc57--