pg_stat_statements: Fix normalization of + signs for FETCH and MOVE
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t139804psql -h localhost -U postgresBuilt from patchset v3 (message #3), August 23, 2026 at 01:00 PM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t139804_3 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t139804_3 && git checkout t139804_3Patchset v3 (message #3) is on t139804_3
Hi,
While revisiting feature “Show sizes of FETCH queries as constants in pg_stat_statements”, I found another issue where “+” is not properly handled.
See this repro:
```
evantest=*# fetch + 1 c;
g
---
(0 rows)
evantest=*# select calls, query from pg_stat_statements where query like 'fetch%';
calls | query
-------+--------------
1 | fetch $1 1 c
(1 row)
evantest=*# SELECT pg_stat_statements_reset();
pg_stat_statements_reset
-------------------------------
2026-06-01 08:59:19.538839+08
(1 row)
evantest=*# fetch +1 c;
g
---
(0 rows)
evantest=*# select calls, query from pg_stat_statements where query like 'fetch%';
calls | query
-------+-------------
1 | fetch $11 c
(1 row)
```
As shown above, the "+" sign is replaced separately from the integer. However, a "-" sign is handled correctly:
```
evantest=*# SELECT pg_stat_statements_reset();
pg_stat_statements_reset
-------------------------------
2026-06-01 09:09:48.776385+08
(1 row)
evantest=*# fetch - 1 c;
g
----
10
(1 row)
evantest=*# select calls, query from pg_stat_statements where query like 'fetch%';
calls | query
-------+------------
1 | fetch $1 c
(1 row)
```
The same issue exists for “MOVE” as well:
```
evantest=*# move relative + 2 c;
MOVE 0
evantest=*# select calls, query from pg_stat_statements where query like 'move%';
calls | query
-------+----------------------
1 | move relative $1 2 c
(1 row)
```
The fix seems simple, as the "-" sign is already handled:
```
/*
* We should find the token position exactly, but if we somehow
* run past it, work with that.
*/
if (yylloc >= loc)
{
if (query[loc] == '-')
{
/*
* It's a negative value - this is the one and only case
* where we replace more than a single token.
*
* Do not compensate for the special-case adjustment of
* location to that of the leading '-' operator in the
* event of a negative constant (see doNegate() in
* gram.y). It is also useful for our purposes to start
* from the minus symbol. In this way, queries like
* "select * from foo where bar = 1" and "select * from
* foo where bar = -2" can be treated similarly.
*/
```
We just need to handle "+" in the same way. See the attached patch for details.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Attachments:
v1-0001-pg_stat_statements-Fix-normalization-of-signs-for.patchapplication/octet-stream; name=v1-0001-pg_stat_statements-Fix-normalization-of-signs-for.patch; x-unix-mode=0644Download+54-13
On Mon, Jun 01, 2026 at 01:33:40PM +0800, Chao Li wrote:
As shown above, the "+" sign is replaced separately from the
integer. However, a "-" sign is handled correctly:
```
We just need to handle "+" in the same way. See the attached patch for details.
This is not directly related to FETCH and MOVE. For example:
=# select +1;
?column?
----------
1
(1 row)
=# select query from pg_stat_statements where query ~ 'select';
query
------------
select +$1
(1 row)
I don't recall somebody complaining about that..
--
Michael
On Jun 1, 2026, at 14:06, Michael Paquier <michael@paquier.xyz> wrote:
On Mon, Jun 01, 2026 at 01:33:40PM +0800, Chao Li wrote:
As shown above, the "+" sign is replaced separately from the
integer. However, a "-" sign is handled correctly:
```
We just need to handle "+" in the same way. See the attached patch for details.This is not directly related to FETCH and MOVE. For example:
=# select +1;
?column?
----------
1
(1 row)
=# select query from pg_stat_statements where query ~ 'select';
query
------------
select +$1
(1 row)I don't recall somebody complaining about that..
--
Michael
Thanks for pointing out the example. I don’t think “select +1” parse “+” in the same way as a FETCH statement.
If we turn on debug_print_raw_parse:
```
evantest=*# set debug_print_raw_parse = 1 and check the raw parse trees:
SET
evantest=*# select +1;
?column?
----------
1
(1 row)
```
This generates a raw parse tree containing:
```
2026-06-01 15:05:08.528 CST [22501] DETAIL: (
{RAWSTMT
:stmt
{SELECTSTMT
:distinctClause <>
:intoClause <>
:targetList (
{RESTARGET
:name <>
:indirection <>
:val
{A_EXPR
:name ("+")
:lexpr <>
:rexpr
{A_CONST
:val 1
:location 8
}
:rexpr_list_start 0
:rexpr_list_end 0
:location 7
}
:location 7
}
)
```
Here, “+” is parsed as a unary operator, A_EXPR’s location is at “+”, and A_CONST’s location is at “1”.
For a FETCH statement:
```
evantest=*# fetch +1 c;
g
---
4
(1 row)
```
The raw parse tree looks like:
```
2026-06-01 15:05:37.993 CST [22501] DETAIL: (
{RAWSTMT
:stmt
{FETCHSTMT
:direction 0
:howMany 1
:portalname c
:ismove false
:direction_keyword 0
:location 6
}
:stmt_location 0
:stmt_len 10
}
)
```
Here, the count is parsed through SignedIconst, and the FETCHSTMT location is at “+".
So, I still think this issue is specific to FETCH and MOVE. However, I realized that the new code comment in my patch was misleading, so I have updated it.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/