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 1vzHlY-000ru6-15 for pgsql-hackers@arkaria.postgresql.org; Sun, 08 Mar 2026 17:18:08 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vzHlU-00BCJp-2q for pgsql-hackers@arkaria.postgresql.org; Sun, 08 Mar 2026 17:18:05 +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 1vzHlU-00BCJh-1c for pgsql-hackers@lists.postgresql.org; Sun, 08 Mar 2026 17:18:05 +0000 Received: from mail-ed1-x52a.google.com ([2a00:1450:4864:20::52a]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vzHlS-00000001YEe-0TkO for pgsql-hackers@postgresql.org; Sun, 08 Mar 2026 17:18:04 +0000 Received: by mail-ed1-x52a.google.com with SMTP id 4fb4d7f45d1cf-65c0891f4e9so3240638a12.1 for ; Sun, 08 Mar 2026 10:18:02 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1772990276; cv=none; d=google.com; s=arc-20240605; b=BsVVeGcQnme2hGXAns2JzY4VMosT3NHyPQhoZKWRPutcNz9fWNjXJp61wkpeapiQYH aOKtaHciakTF3JHHwQO7KE1LYmLYPoMbxl1qBQhD/3GSefzuWza1spuMp3VkhKxXiilM 13ppKqH6Ipjl66J247qkpoGd+iexA/Tv79AWnnwUPRb5RIdXLW7zLui+NU6idqqGTihy 6h43mKX0XiajIqJcbhK28DVLDa4Ry1VE7qUj/speId7hKBmx7s0GC5P6DgjvjOd8GMxm FC5beVmRy74GLiIFyU4TfaLqK697mG93v/i3M7ICskCoJrcZ67cTEq2hhFDm6njbEMGd mi4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=0V32esgbjU/DPIxzxZ29OsT1TpWXHp/lQkR7j5InPFc=; fh=qSJsZD8AMIawn1/Kh/UckT9T7ONulmOmBrtK2ismc4Y=; b=fAD63UUQkFmu2Q1873DiScUXnLSU7FgfqJYXBF+owgeykYXKmuEn88tMJyRYN5grFq xmgU1p+uRapcd+A6bSEzCQzsqEISpX1SS9aSSUGUl4u9vPzV3SI1W8omb9aHvzZcAHyp 9hlxN4MA6tHIEfvoDVGhnNro5hTeoity5bqhvIESI97E4gEhLgwOr4LEwlXGe/72N8xk vvHyhfbRDiWTeNdHjBvFx7Eqf0eta267NBK4bap86CbISVqY0TYCuSc8LS/0z4pZ8ozk V5qM0BX8tiANo4Q2MTD6UXTKKvCvXlXSQNA8vjRInfRA7zRcmxIqUFGry7PeOlS1sHeW 2AHw==; darn=postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772990276; x=1773595076; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=0V32esgbjU/DPIxzxZ29OsT1TpWXHp/lQkR7j5InPFc=; b=LLUKmvh+HgpZoTgvJIk7Ypo7O2TIGyev9B7voTm5swgRr5MyPw+UIAEdZqZ/KJA52J 4JseGRuwchc9a7DrsGbJn/eiNyiJhCcM4dkOWfdRMonO/+AXLn16TuksG2hDfeTnMzHH vjseMsUDaL7wpABip3hcCiwiGR3Xzcw7FSqQ6MufH16YC64fTKRs8+CxtR7zMIrTYRgN 2F1zzBMIxkaAHyXEUTzn/fj5j7DS06tNa4IaY+wmiZZrHhdw6KC7sg3iKGfOc/uAHkBS EpvLNpVd4Tp14h8Vial1mJSkFvsP/AQ5BF3HdKmjdCQg4z7n6kZ6cnjco9U5XEk7lFdn 1fDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772990276; x=1773595076; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=0V32esgbjU/DPIxzxZ29OsT1TpWXHp/lQkR7j5InPFc=; b=Qc2tDIeo31VOyghByE1/NOEib9Ni3vWRXFfEXeiree5w6PQ9WiHyPWQ2ga8Qc7E4ob ht7pYdY3oPYp7ktpYPCh1z7f3mOorklcU366iXOWCE8hpIJcMlj7y+rmeOXbP40IZJQM FoJFYzrjhKeg6B03HDkBBJlR0IJ7jWa0gONR5wuVLffBiumzqcvMmz1JOqELrRpIK8xg llZF3J9oYSngTyz3fxNuybr4HVfvmQj+mNKJjZwOmB3aiW0wwr6FgFPBcOF/+CGV73Ze oikqM4oNquwt+DJqoyG7fHYsip5P2cJXJQiZUQDnGy8zAFv95kdwFjoRdEa4OIwpeBwb tibQ== X-Gm-Message-State: AOJu0YxQhSIXJS1Bh7BerFMV7D0s88F3/uQGaxbV2Abyol1yYScZdFHr kbwxuJeAnAqJG62V05bH5luMH0t9SM1UvPRJnv6gHWZS4SrJSy8R5mCY1rVvbYi6mW9JthrumZw iMZD8CDE2SSWL+Lyl42uqo6b5LLC4204HJpROJvM= X-Gm-Gg: ATEYQzwEwnqw71DVceBF2B+nYi+VBOG5MnMVrWrjRT+VmDWRuZPAUh90ugbDE1wSzgB tw3ZIiy2Qt5F+Mj0+V/wdce9Ez7gjQIun7YndsmY2ICpBh+Y8w8ZXFIISWvlOKralhJN7eVtyif V57jOlmg8aQbosoEgFebBldDCV32htb4FKR3yZlR9fcU8wzivr9t9X111tyRVfAJ5srfmd2kF8h tO8rLgFU9h9xxYV8McFG1I6wUmfXx3lmoOoKBkZf0hJk12jrt6+790dZl3p/OkfNvRnKxWl6uav BkDKyZmsYMzgvTjOx3T+DRER6BvNc5bYqnSQDVlV3ERXesI7 X-Received: by 2002:a17:907:608c:b0:b87:1edc:5a27 with SMTP id a640c23a62f3a-b942da495dbmr454026666b.1.1772990276111; Sun, 08 Mar 2026 10:17:56 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Alexandre Felipe Date: Sun, 8 Mar 2026 17:17:44 +0000 X-Gm-Features: AaiRm50xqhHp4a9rdg24-Y32b50g5pNspvWA4OWZbRO9ZoiodCWPo6uVNl0C6pI Message-ID: Subject: Re: tid_blockno() and tid_offset() accessor functions To: Ayush Tiwari Cc: pgsql-hackers@postgresql.org Content-Type: multipart/alternative; boundary="000000000000991466064c8677b0" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000991466064c8677b0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable That was something I was surprised to learn, that we can check TID, do queries by TID intervals, but we can't get pages from TID, when I was trying to analyse how many pages on average a certain query would touch for different users. I think it would be nice to also support SELECT * FROM table WHERE tid_block(tid) BETWEEN b1 AND b2; I wouldn't bother to support block number above 2^31 or block offsets above 2^15. This test shows that it assumes wrapping -- (-1,0) wraps to blockno 4294967295 SELECT tid_block('(-1,0)'::tid); tid_block ------------ 4294967295 You could just stick with that, I am sure that someone with a table having more than 2B pages on a table will understand that. for tid_offset I don't think it is even possible. If the maximum page size is limited to 2^15, must have a header and each offset has a line pointer aren't offsets limited to something smaller than 2^13? Regards On Sat, Mar 7, 2026 at 7:43=E2=80=AFPM Ayush Tiwari wrote: > Hello, > > Attaching a V2-patch post rebasing due to oid conflict with the latest > main branch. In addition to that changing the sql function name for tid > block number to tid_block and adding document related changes. > > Please review and let me know your thoughts. > > Regards, > Ayush > > On Sat, 28 Feb 2026 at 00:29, Ayush Tiwari > wrote: > >> Hi hackers, >> >> As of now we don't have any built-in way to extract the block and offset >> components from a TID. When people need to group by page (like for bloat >> analysis) or filter by specific blocks, they usually end up using the >> `ctid::text::point` hack: >> >> SELECT (ctid::text::point)[0]::bigint AS blockno, >> (ctid::text::point)[1]::int AS offset >> FROM my_table; >> >> This works, but it's pretty clunky, relies on the text representation, >> and isn't great if you're trying to parse TIDs outside of SQL. >> >> The attached patch adds two simple accessor functions: >> - `tid_blockno(tid) -> bigint` >> - `tid_offset(tid) -> integer` >> >> A couple of quick notes on the implementation I went for: >> - `tid_blockno` returns `int8` since `BlockNumber` is `uint32` and could >> overflow `int4`. >> - `tid_offset` returns `int4` since `OffsetNumber` is `uint16`. >> - Both are marked leakproof and strict. >> - I used the `NoCheck` macros from `itemptr.h` so they safely handle >> user-supplied literals like `(0,0)`. >> >> Please let me know what you think! >> >> Regards, >> Ayush >> > --000000000000991466064c8677b0 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
That was something I was surprised to learn, that we can c= heck TID, do queries by TID intervals, but we can't get pages from TID,= when I was trying to analyse how many pages on average a certain query wou= ld touch for different users.
I think it would be nice to also support= =C2=A0
SELECT * FROM table WHERE tid_block(tid) BETWEEN b1 AND b2= ;

I wouldn't bother to support block number above 2^= 31 or block offsets above 2^15.

This test shows that it= assumes wrapping
-- (-1,0) wraps to blockno 4294967295
SELECT= tid_block('(-1,0)'::tid);
=C2=A0tid_block =C2=A0
-----------= -
=C2=A04294967295

You could just stick with that, I am sure that= someone with a table having more than 2B pages on a table will understand = that.
for tid_offset I don't think it is even possible. If th= e maximum page size is limited to 2^15, must have a header and each offset = has a line pointer aren't offsets limited to something smaller than 2^1= 3?


Regards

On Sat, Mar 7, = 2026 at 7:43=E2=80=AFPM Ayush Tiwari <ayushtiwari.slg01@gmail.com> wrote:
= Hello,

Attaching a V2-patch post rebasing due to oid conflict with t= he latest main branch. In addition to that changing the sql function name f= or tid block number to tid_block and adding document related changes.
Please=C2=A0review and let me know your thoughts.

Regards,
Ayus= h

On Sat, 28 Feb 2026 at 00:29, Ayush Tiwari <ayushtiwari.slg01@gmail.com> w= rote:
Hi hackers,

As of now we don't have any built-in way to= extract the block and offset components from a TID. When people need to gr= oup by page (like for bloat analysis) or filter by specific blocks, they us= ually end up using the `ctid::text::point` hack:

=C2=A0 =C2=A0 SELEC= T (ctid::text::point)[0]::bigint AS blockno,
=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0(ctid::text::point)[1]::int =C2=A0 =C2=A0AS offset
=C2=A0 = =C2=A0 FROM my_table;

This works, but it's pretty clunky, relies= on the text representation, and isn't great if you're trying to pa= rse TIDs outside of SQL.

The attached patch adds two simple accessor= functions:
- `tid_blockno(tid) -> bigint`
- `tid_offset(tid) ->= ; integer`

A couple of quick notes on the implementation I went for:=
- `tid_blockno` returns `int8` since `BlockNumber` is `uint32` and coul= d overflow `int4`.
- `tid_offset` returns `int4` since `OffsetNumber` is= `uint16`.
- Both are marked leakproof and strict.
- I used the `NoCh= eck` macros from `itemptr.h` so they safely handle user-supplied literals l= ike `(0,0)`.

Please let me know what you think!

Regards,
A= yush
--000000000000991466064c8677b0--