mirror of
https://github.com/postgres/postgres.git
synced 2025-06-27 23:21:58 +03:00
Fix squashing algorithm for query texts
The algorithm to squash lists of constants added by commit 62d712ecfd
was a bit too simplistic; we wanted to avoid adding unnecessary
complexity, but cases like direct function calls of typecasting
functions (and others) were missed, and bogus SQL syntax was being shown
in pg_stat_statements normalized query text field. To fix normalization
for those cases, we need the parser to transmit information about were
each list of constant values starts and ends, so add that to a couple of
nodes. Also add a few more test cases to make sure we're doing the
right thing.
The patch initially submitted by Sami added a new private struct in
gram.y to carry the start/end information for A_Expr, but I (Álvaro)
decided that a better fix was to remove the parser indirection via the
in_expr production, and instead create separate components in the a_expr
rule. I'm surprised that this works and doesn't require more changes,
but I assume (without checking) that the grammar used to be more complex
and got simplified at some point.
Bump catversion.
Author: Sami Imseih <samimseih@gmail.com>
Author: Dmitry Dolgov <9erthalion6@gmail.com>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/CAA5RZ0tRXoPG2y6bMgBCWNDt0Tn=unRerbzYM=oW0syi1=C1OA@mail.gmail.com
This commit is contained in:
@ -267,6 +267,36 @@ SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
|||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 0
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 0
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
|
-- with the last element being an explicit function call with an argument, ensure
|
||||||
|
-- the normalization of the squashing interval is correct.
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 IN (1, int4(1), int4(2));
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2)]);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
------------------------------------------------------------------------+-------
|
||||||
|
SELECT WHERE $1 IN ($2 /*, ... */) | 2
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- queries with locking clauses
|
-- queries with locking clauses
|
||||||
--
|
--
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
-- Const squashing functionality
|
-- Const squashing functionality
|
||||||
--
|
--
|
||||||
CREATE EXTENSION pg_stat_statements;
|
CREATE EXTENSION pg_stat_statements;
|
||||||
|
--
|
||||||
|
-- Simple Lists
|
||||||
|
--
|
||||||
CREATE TABLE test_squash (id int, data int);
|
CREATE TABLE test_squash (id int, data int);
|
||||||
-- IN queries
|
-- single element will not be squashed
|
||||||
-- Normal scenario, too many simple constants for an IN query
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
@ -16,42 +18,150 @@ SELECT * FROM test_squash WHERE id IN (1);
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT ARRAY[1];
|
||||||
|
array
|
||||||
|
-------
|
||||||
|
{1}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
----------------------------------------------------+-------
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1) | 1
|
||||||
|
SELECT ARRAY[$1] | 1
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- more than 1 element in a list will be squashed
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT ARRAY[1, 2, 3];
|
||||||
|
array
|
||||||
|
---------
|
||||||
|
{1,2,3}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT ARRAY[1, 2, 3, 4];
|
||||||
|
array
|
||||||
|
-----------
|
||||||
|
{1,2,3,4}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT ARRAY[1, 2, 3, 4, 5];
|
||||||
|
array
|
||||||
|
-------------
|
||||||
|
{1,2,3,4,5}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
-------------------------------------------------------+-------
|
-------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 1
|
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 3
|
||||||
SELECT * FROM test_squash WHERE id IN ($1) | 1
|
SELECT ARRAY[$1 /*, ... */] | 3
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
|
-- built-in functions will be squashed
|
||||||
|
-- the IN and ARRAY forms of this statement will have the same queryId
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 IN (1, int4(1), int4(2), 2);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2), 2]);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
----------------------------------------------------+-------
|
||||||
|
SELECT WHERE $1 IN ($2 /*, ... */) | 2
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- external parameters will not be squashed
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) \bind 1 2 3 4 5
|
||||||
|
;
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) \bind 1 2 3 4 5
|
||||||
id | data
|
;
|
||||||
----+------
|
|
||||||
(0 rows)
|
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
------------------------------------------------------------------------+-------
|
---------------------------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) | 4
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) | 1
|
||||||
SELECT * FROM test_squash WHERE id IN ($1) | 1
|
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) | 1
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C" | 1
|
(3 rows)
|
||||||
(4 rows)
|
|
||||||
|
-- neither are prepared statements
|
||||||
|
-- the IN and ARRAY forms of this statement will have the same queryId
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
PREPARE p1(int, int, int, int, int) AS
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5);
|
||||||
|
EXECUTE p1(1, 2, 3, 4, 5);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DEALLOCATE p1;
|
||||||
|
PREPARE p1(int, int, int, int, int) AS
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY[$1, $2, $3, $4, $5]);
|
||||||
|
EXECUTE p1(1, 2, 3, 4, 5);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
DEALLOCATE p1;
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
------------------------------------------------------------+-------
|
||||||
|
DEALLOCATE $1 | 2
|
||||||
|
PREPARE p1(int, int, int, int, int) AS +| 2
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) |
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
-- More conditions in the query
|
-- More conditions in the query
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
@ -75,10 +185,25 @@ SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) AND da
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]) AND data = 2;
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) AND data = 2;
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) AND data = 2;
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
---------------------------------------------------------------------+-------
|
---------------------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) AND data = $2 | 3
|
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */) AND data = $2 | 6
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
@ -107,24 +232,46 @@ SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
-------------------------------------------------------+-------
|
-------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */)+| 3
|
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */)+| 6
|
||||||
AND data IN ($2 /*, ... */) |
|
AND data IN ($2 /*, ... */) |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- No constants simplification for OpExpr
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- In the following two queries the operator expressions (+) and (@) have
|
-- No constants squashing for OpExpr
|
||||||
-- different oppno, and will be given different query_id if squashed, even though
|
-- The IN and ARRAY forms of this statement will have the same queryId
|
||||||
-- the normalized query will be the same
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN
|
SELECT * FROM test_squash WHERE id IN
|
||||||
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
|
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
|
||||||
id | data
|
id | data
|
||||||
@ -137,19 +284,35 @@ SELECT * FROM test_squash WHERE id IN
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY
|
||||||
|
[1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY
|
||||||
|
[@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9']);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------------------------------------------------------+-------
|
----------------------------------------------------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN +| 1
|
SELECT * FROM test_squash WHERE id IN +| 2
|
||||||
($1 + $2, $3 + $4, $5 + $6, $7 + $8, $9 + $10, $11 + $12, $13 + $14, $15 + $16, $17 + $18) |
|
($1 + $2, $3 + $4, $5 + $6, $7 + $8, $9 + $10, $11 + $12, $13 + $14, $15 + $16, $17 + $18) |
|
||||||
SELECT * FROM test_squash WHERE id IN +| 1
|
SELECT * FROM test_squash WHERE id IN +| 2
|
||||||
(@ $1, @ $2, @ $3, @ $4, @ $5, @ $6, @ $7, @ $8, @ $9) |
|
(@ $1, @ $2, @ $3, @ $4, @ $5, @ $6, @ $7, @ $8, @ $9) |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(3 rows)
|
(3 rows)
|
||||||
|
|
||||||
|
--
|
||||||
-- FuncExpr
|
-- FuncExpr
|
||||||
|
--
|
||||||
-- Verify multiple type representation end up with the same query_id
|
-- Verify multiple type representation end up with the same query_id
|
||||||
CREATE TABLE test_float (data float);
|
CREATE TABLE test_float (data float);
|
||||||
|
-- The casted ARRAY expressions will have the same queryId as the IN clause
|
||||||
|
-- form of the query
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
@ -181,12 +344,38 @@ SELECT data FROM test_float WHERE data IN (1.0, 1.0);
|
|||||||
------
|
------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY['1'::double precision, '2'::double precision]);
|
||||||
|
data
|
||||||
|
------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1.0::double precision, 1.0::double precision]);
|
||||||
|
data
|
||||||
|
------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, 2]);
|
||||||
|
data
|
||||||
|
------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, '2']);
|
||||||
|
data
|
||||||
|
------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY['1', 2]);
|
||||||
|
data
|
||||||
|
------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
-----------------------------------------------------------+-------
|
--------------------------------------------------------------------+-------
|
||||||
SELECT data FROM test_float WHERE data IN ($1 /*, ... */) | 5
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[$1 /*, ... */]) | 3
|
||||||
|
SELECT data FROM test_float WHERE data IN ($1 /*, ... */) | 7
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(3 rows)
|
||||||
|
|
||||||
-- Numeric type, implicit cast is squashed
|
-- Numeric type, implicit cast is squashed
|
||||||
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
|
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
|
||||||
@ -201,12 +390,18 @@ SELECT * FROM test_squash_numeric WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
-----------------------------------------------------------------+-------
|
--------------------------------------------------------------------------+-------
|
||||||
|
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[$1 /*, ... */]) | 1
|
||||||
SELECT * FROM test_squash_numeric WHERE data IN ($1 /*, ... */) | 1
|
SELECT * FROM test_squash_numeric WHERE data IN ($1 /*, ... */) | 1
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(3 rows)
|
||||||
|
|
||||||
-- Bigint, implicit cast is squashed
|
-- Bigint, implicit cast is squashed
|
||||||
CREATE TABLE test_squash_bigint (id int, data bigint);
|
CREATE TABLE test_squash_bigint (id int, data bigint);
|
||||||
@ -221,14 +416,20 @@ SELECT * FROM test_squash_bigint WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------------------+-------
|
-------------------------------------------------------------------------+-------
|
||||||
|
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[$1 /*, ... */]) | 1
|
||||||
SELECT * FROM test_squash_bigint WHERE data IN ($1 /*, ... */) | 1
|
SELECT * FROM test_squash_bigint WHERE data IN ($1 /*, ... */) | 1
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(3 rows)
|
||||||
|
|
||||||
-- Bigint, explicit cast is not squashed
|
-- Bigint, explicit cast is squashed
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
@ -242,15 +443,22 @@ SELECT * FROM test_squash_bigint WHERE data IN
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[
|
||||||
|
1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
|
||||||
|
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------+-------
|
----------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash_bigint WHERE data IN +| 1
|
SELECT * FROM test_squash_bigint WHERE data IN +| 2
|
||||||
($1 /*, ... */::bigint) |
|
($1 /*, ... */) |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- Bigint, long tokens with parenthesis
|
-- Bigint, long tokens with parenthesis, will not squash
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
@ -264,44 +472,47 @@ SELECT * FROM test_squash_bigint WHERE id IN
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT * FROM test_squash_bigint WHERE id = ANY(ARRAY[
|
||||||
query | calls
|
abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
|
||||||
-------------------------------------------------------------------------+-------
|
abs(800), abs(900), abs(1000), ((abs(1100)))]);
|
||||||
SELECT * FROM test_squash_bigint WHERE id IN +| 1
|
|
||||||
(abs($1), abs($2), abs($3), abs($4), abs($5), abs($6), abs($7),+|
|
|
||||||
abs($8), abs($9), abs($10), ((abs($11)))) |
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
|
||||||
(2 rows)
|
|
||||||
|
|
||||||
-- CoerceViaIO, SubLink instead of a Const
|
|
||||||
CREATE TABLE test_squash_jsonb (id int, data jsonb);
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
|
||||||
t
|
|
||||||
---
|
|
||||||
t
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN
|
|
||||||
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
|
||||||
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
|
||||||
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
|
||||||
(SELECT '"10"')::jsonb);
|
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------------------------+-------
|
-------------------------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN +| 1
|
SELECT * FROM test_squash_bigint WHERE id IN +| 2
|
||||||
((SELECT $1)::jsonb, (SELECT $2)::jsonb, (SELECT $3)::jsonb,+|
|
(abs($1), abs($2), abs($3), abs($4), abs($5), abs($6), abs($7),+|
|
||||||
(SELECT $4)::jsonb, (SELECT $5)::jsonb, (SELECT $6)::jsonb,+|
|
abs($8), abs($9), abs($10), ((abs($11)))) |
|
||||||
(SELECT $7)::jsonb, (SELECT $8)::jsonb, (SELECT $9)::jsonb,+|
|
|
||||||
(SELECT $10)::jsonb) |
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
-- Multiple FuncExpr's. Will not squash
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 IN (1::int::bigint::int, 2::int::bigint::int);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 = ANY(ARRAY[1::int::bigint::int, 2::int::bigint::int]);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
----------------------------------------------------+-------
|
||||||
|
SELECT WHERE $1 IN ($2 /*, ... */) | 2
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
--
|
||||||
-- CoerceViaIO
|
-- CoerceViaIO
|
||||||
|
--
|
||||||
-- Create some dummy type to force CoerceViaIO
|
-- Create some dummy type to force CoerceViaIO
|
||||||
CREATE TYPE casttesttype;
|
CREATE TYPE casttesttype;
|
||||||
CREATE FUNCTION casttesttype_in(cstring)
|
CREATE FUNCTION casttesttype_in(cstring)
|
||||||
@ -349,15 +560,25 @@ SELECT * FROM test_squash_cast WHERE data IN
|
|||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_cast WHERE data = ANY (ARRAY
|
||||||
|
[1::int4::casttesttype, 2::int4::casttesttype, 3::int4::casttesttype,
|
||||||
|
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
|
||||||
|
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
|
||||||
|
10::int4::casttesttype, 11::int4::casttesttype]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------+-------
|
----------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash_cast WHERE data IN +| 1
|
SELECT * FROM test_squash_cast WHERE data IN +| 2
|
||||||
($1 /*, ... */::int4::casttesttype) |
|
($1 /*, ... */) |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- Some casting expression are simplified to Const
|
-- Some casting expression are simplified to Const
|
||||||
|
CREATE TABLE test_squash_jsonb (id int, data jsonb);
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
@ -366,8 +587,16 @@ SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
|||||||
|
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN
|
SELECT * FROM test_squash_jsonb WHERE data IN
|
||||||
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
||||||
( '"5"')::jsonb, ( '"6"')::jsonb, ( '"7"')::jsonb, ( '"8"')::jsonb,
|
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
|
||||||
( '"9"')::jsonb, ( '"10"')::jsonb);
|
('"9"')::jsonb, ('"10"')::jsonb);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_jsonb WHERE data = ANY (ARRAY
|
||||||
|
[('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
||||||
|
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
|
||||||
|
('"9"')::jsonb, ('"10"')::jsonb]);
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
@ -375,27 +604,143 @@ SELECT * FROM test_squash_jsonb WHERE data IN
|
|||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------+-------
|
----------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN +| 1
|
SELECT * FROM test_squash_jsonb WHERE data IN +| 2
|
||||||
(($1 /*, ... */)::jsonb) |
|
($1 /*, ... */) |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
-- RelabelType
|
-- CoerceViaIO, SubLink instead of a Const. Will not squash
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
|
SELECT * FROM test_squash_jsonb WHERE data IN
|
||||||
|
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
||||||
|
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
||||||
|
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
||||||
|
(SELECT '"10"')::jsonb);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash_jsonb WHERE data = ANY(ARRAY
|
||||||
|
[(SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
||||||
|
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
||||||
|
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
||||||
|
(SELECT '"10"')::jsonb]);
|
||||||
id | data
|
id | data
|
||||||
----+------
|
----+------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
------------------------------------------------------------+-------
|
----------------------------------------------------------------------+-------
|
||||||
SELECT * FROM test_squash WHERE id IN ($1 /*, ... */::oid) | 1
|
SELECT * FROM test_squash_jsonb WHERE data IN +| 2
|
||||||
|
((SELECT $1)::jsonb, (SELECT $2)::jsonb, (SELECT $3)::jsonb,+|
|
||||||
|
(SELECT $4)::jsonb, (SELECT $5)::jsonb, (SELECT $6)::jsonb,+|
|
||||||
|
(SELECT $7)::jsonb, (SELECT $8)::jsonb, (SELECT $9)::jsonb,+|
|
||||||
|
(SELECT $10)::jsonb) |
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Multiple CoerceViaIO wrapping a constant. Will not squash
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 IN (1::text::int::text::int, 1::text::int::text::int);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT WHERE 1 = ANY(ARRAY[1::text::int::text::int, 1::text::int::text::int]);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
-------------------------------------------------------------------------+-------
|
||||||
|
SELECT WHERE $1 IN ($2::text::int::text::int, $3::text::int::text::int) | 2
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- RelabelType
|
||||||
|
--
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- if there is only one level of RelabelType, the list will be squashable
|
||||||
|
SELECT * FROM test_squash WHERE id IN
|
||||||
|
(1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT ARRAY[1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid];
|
||||||
|
array
|
||||||
|
---------------------
|
||||||
|
{1,2,3,4,5,6,7,8,9}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- if there is at least one element with multiple levels of RelabelType,
|
||||||
|
-- the list will not be squashable
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid::int::oid);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY[1::oid, 2::oid::int::oid]);
|
||||||
|
id | data
|
||||||
|
----+------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
--------------------------------------------------------------------+-------
|
||||||
|
SELECT * FROM test_squash WHERE id IN +| 1
|
||||||
|
($1 /*, ... */) |
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1::oid, $2::oid::int::oid) | 2
|
||||||
|
SELECT ARRAY[$1 /*, ... */] | 1
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- edge cases
|
||||||
|
--
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- for nested arrays, only constants are squashed
|
||||||
|
SELECT ARRAY[
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
];
|
||||||
|
array
|
||||||
|
-----------------------------------------------------------------------------------------------
|
||||||
|
{{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10},{1,2,3,4,5,6,7,8,9,10}}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
----------------------------------------------------+-------
|
||||||
|
SELECT ARRAY[ +| 1
|
||||||
|
ARRAY[$1 /*, ... */], +|
|
||||||
|
ARRAY[$2 /*, ... */], +|
|
||||||
|
ARRAY[$3 /*, ... */], +|
|
||||||
|
ARRAY[$4 /*, ... */] +|
|
||||||
|
] |
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
@ -409,23 +754,59 @@ FROM cte;
|
|||||||
--------
|
--------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
-- Simple array would be squashed as well
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
t
|
t
|
||||||
---
|
---
|
||||||
t
|
t
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
-- Rewritten as an OpExpr, so it will not be squashed
|
||||||
array
|
select where '1' IN ('1'::int, '2'::int::text);
|
||||||
------------------------
|
--
|
||||||
{1,2,3,4,5,6,7,8,9,10}
|
(1 row)
|
||||||
|
|
||||||
|
-- Rewritten as an ArrayExpr, so it will be squashed
|
||||||
|
select where '1' IN ('1'::int, '2'::int);
|
||||||
|
--
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
query | calls
|
query | calls
|
||||||
----------------------------------------------------+-------
|
----------------------------------------------------+-------
|
||||||
SELECT ARRAY[$1 /*, ... */] | 1
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
select where $1 IN ($2 /*, ... */) | 1
|
||||||
|
select where $1 IN ($2::int, $3::int::text) | 1
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
t
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Both of these queries will be rewritten as an ArrayExpr, so they
|
||||||
|
-- will be squashed, and have a similar queryId
|
||||||
|
select where '1' IN ('1'::int::text, '2'::int::text);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select where '1' = ANY (array['1'::int::text, '2'::int::text]);
|
||||||
|
--
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
query | calls
|
||||||
|
----------------------------------------------------+-------
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t | 1
|
||||||
|
select where $1 IN ($2 /*, ... */) | 2
|
||||||
(2 rows)
|
(2 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- cleanup
|
||||||
|
--
|
||||||
|
DROP TABLE test_squash;
|
||||||
|
DROP TABLE test_float;
|
||||||
|
DROP TABLE test_squash_numeric;
|
||||||
|
DROP TABLE test_squash_bigint;
|
||||||
|
DROP TABLE test_squash_cast CASCADE;
|
||||||
|
DROP TABLE test_squash_jsonb;
|
||||||
|
@ -2810,14 +2810,12 @@ generate_normalized_query(JumbleState *jstate, const char *query,
|
|||||||
{
|
{
|
||||||
char *norm_query;
|
char *norm_query;
|
||||||
int query_len = *query_len_p;
|
int query_len = *query_len_p;
|
||||||
int i,
|
int norm_query_buflen, /* Space allowed for norm_query */
|
||||||
norm_query_buflen, /* Space allowed for norm_query */
|
|
||||||
len_to_wrt, /* Length (in bytes) to write */
|
len_to_wrt, /* Length (in bytes) to write */
|
||||||
quer_loc = 0, /* Source query byte location */
|
quer_loc = 0, /* Source query byte location */
|
||||||
n_quer_loc = 0, /* Normalized query byte location */
|
n_quer_loc = 0, /* Normalized query byte location */
|
||||||
last_off = 0, /* Offset from start for previous tok */
|
last_off = 0, /* Offset from start for previous tok */
|
||||||
last_tok_len = 0; /* Length (in bytes) of that tok */
|
last_tok_len = 0; /* Length (in bytes) of that tok */
|
||||||
bool in_squashed = false; /* in a run of squashed consts? */
|
|
||||||
int num_constants_replaced = 0;
|
int num_constants_replaced = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2832,16 +2830,13 @@ generate_normalized_query(JumbleState *jstate, const char *query,
|
|||||||
* certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
|
* certainly isn't more than 11 bytes, even if n reaches INT_MAX. We
|
||||||
* could refine that limit based on the max value of n for the current
|
* could refine that limit based on the max value of n for the current
|
||||||
* query, but it hardly seems worth any extra effort to do so.
|
* query, but it hardly seems worth any extra effort to do so.
|
||||||
*
|
|
||||||
* Note this also gives enough room for the commented-out ", ..." list
|
|
||||||
* syntax used by constant squashing.
|
|
||||||
*/
|
*/
|
||||||
norm_query_buflen = query_len + jstate->clocations_count * 10;
|
norm_query_buflen = query_len + jstate->clocations_count * 10;
|
||||||
|
|
||||||
/* Allocate result buffer */
|
/* Allocate result buffer */
|
||||||
norm_query = palloc(norm_query_buflen + 1);
|
norm_query = palloc(norm_query_buflen + 1);
|
||||||
|
|
||||||
for (i = 0; i < jstate->clocations_count; i++)
|
for (int i = 0; i < jstate->clocations_count; i++)
|
||||||
{
|
{
|
||||||
int off, /* Offset from start for cur tok */
|
int off, /* Offset from start for cur tok */
|
||||||
tok_len; /* Length (in bytes) of that tok */
|
tok_len; /* Length (in bytes) of that tok */
|
||||||
@ -2856,65 +2851,24 @@ generate_normalized_query(JumbleState *jstate, const char *query,
|
|||||||
if (tok_len < 0)
|
if (tok_len < 0)
|
||||||
continue; /* ignore any duplicates */
|
continue; /* ignore any duplicates */
|
||||||
|
|
||||||
/*
|
/* Copy next chunk (what precedes the next constant) */
|
||||||
* What to do next depends on whether we're squashing constant lists,
|
|
||||||
* and whether we're already in a run of such constants.
|
|
||||||
*/
|
|
||||||
if (!jstate->clocations[i].squashed)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This location corresponds to a constant not to be squashed.
|
|
||||||
* Print what comes before the constant ...
|
|
||||||
*/
|
|
||||||
len_to_wrt = off - last_off;
|
|
||||||
len_to_wrt -= last_tok_len;
|
|
||||||
|
|
||||||
Assert(len_to_wrt >= 0);
|
|
||||||
|
|
||||||
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
|
|
||||||
n_quer_loc += len_to_wrt;
|
|
||||||
|
|
||||||
/* ... and then a param symbol replacing the constant itself */
|
|
||||||
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
|
|
||||||
num_constants_replaced++ + 1 + jstate->highest_extern_param_id);
|
|
||||||
|
|
||||||
/* In case previous constants were merged away, stop doing that */
|
|
||||||
in_squashed = false;
|
|
||||||
}
|
|
||||||
else if (!in_squashed)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* This location is the start position of a run of constants to be
|
|
||||||
* squashed, so we need to print the representation of starting a
|
|
||||||
* group of stashed constants.
|
|
||||||
*
|
|
||||||
* Print what comes before the constant ...
|
|
||||||
*/
|
|
||||||
len_to_wrt = off - last_off;
|
len_to_wrt = off - last_off;
|
||||||
len_to_wrt -= last_tok_len;
|
len_to_wrt -= last_tok_len;
|
||||||
Assert(len_to_wrt >= 0);
|
Assert(len_to_wrt >= 0);
|
||||||
Assert(i + 1 < jstate->clocations_count);
|
|
||||||
Assert(jstate->clocations[i + 1].squashed);
|
|
||||||
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
|
memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt);
|
||||||
n_quer_loc += len_to_wrt;
|
n_quer_loc += len_to_wrt;
|
||||||
|
|
||||||
/* ... and then start a run of squashed constants */
|
|
||||||
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d /*, ... */",
|
|
||||||
num_constants_replaced++ + 1 + jstate->highest_extern_param_id);
|
|
||||||
|
|
||||||
/* The next location will match the block below, to end the run */
|
|
||||||
in_squashed = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
* The second location of a run of squashable elements; this
|
* And insert a param symbol in place of the constant token; and, if
|
||||||
* indicates its end.
|
* we have a squashable list, insert a placeholder comment starting
|
||||||
|
* from the list's second value.
|
||||||
*/
|
*/
|
||||||
in_squashed = false;
|
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d%s",
|
||||||
}
|
num_constants_replaced + 1 + jstate->highest_extern_param_id,
|
||||||
|
jstate->clocations[i].squashed ? " /*, ... */" : "");
|
||||||
|
num_constants_replaced++;
|
||||||
|
|
||||||
/* Otherwise the constant is squashed away -- move forward */
|
/* move forward */
|
||||||
quer_loc = off + tok_len;
|
quer_loc = off + tok_len;
|
||||||
last_off = off;
|
last_off = off;
|
||||||
last_tok_len = tok_len;
|
last_tok_len = tok_len;
|
||||||
@ -3005,6 +2959,9 @@ fill_in_constant_lengths(JumbleState *jstate, const char *query,
|
|||||||
|
|
||||||
Assert(loc >= 0);
|
Assert(loc >= 0);
|
||||||
|
|
||||||
|
if (locs[i].squashed)
|
||||||
|
continue; /* squashable list, ignore */
|
||||||
|
|
||||||
if (loc <= last_loc)
|
if (loc <= last_loc)
|
||||||
continue; /* Duplicate constant, ignore */
|
continue; /* Duplicate constant, ignore */
|
||||||
|
|
||||||
|
@ -87,6 +87,14 @@ SELECT WHERE (1, 2) IN ((1, 2), (2, 3));
|
|||||||
SELECT WHERE (3, 4) IN ((5, 6), (8, 7));
|
SELECT WHERE (3, 4) IN ((5, 6), (8, 7));
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
-- with the last element being an explicit function call with an argument, ensure
|
||||||
|
-- the normalization of the squashing interval is correct.
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
SELECT WHERE 1 IN (1, int4(1), int4(2));
|
||||||
|
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2)]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
--
|
--
|
||||||
-- queries with locking clauses
|
-- queries with locking clauses
|
||||||
--
|
--
|
||||||
|
@ -3,101 +3,160 @@
|
|||||||
--
|
--
|
||||||
CREATE EXTENSION pg_stat_statements;
|
CREATE EXTENSION pg_stat_statements;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Simple Lists
|
||||||
|
--
|
||||||
|
|
||||||
CREATE TABLE test_squash (id int, data int);
|
CREATE TABLE test_squash (id int, data int);
|
||||||
|
|
||||||
-- IN queries
|
-- single element will not be squashed
|
||||||
|
|
||||||
-- Normal scenario, too many simple constants for an IN query
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash WHERE id IN (1);
|
SELECT * FROM test_squash WHERE id IN (1);
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
|
SELECT ARRAY[1];
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
|
-- more than 1 element in a list will be squashed
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3);
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4);
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5);
|
||||||
|
SELECT ARRAY[1, 2, 3];
|
||||||
|
SELECT ARRAY[1, 2, 3, 4];
|
||||||
|
SELECT ARRAY[1, 2, 3, 4, 5];
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
-- built-in functions will be squashed
|
||||||
|
-- the IN and ARRAY forms of this statement will have the same queryId
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
SELECT WHERE 1 IN (1, int4(1), int4(2), 2);
|
||||||
|
SELECT WHERE 1 = ANY (ARRAY[1, int4(1), int4(2), 2]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
-- external parameters will not be squashed
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5) \bind 1 2 3 4 5
|
||||||
|
;
|
||||||
|
SELECT * FROM test_squash WHERE id::text = ANY(ARRAY[$1, $2, $3, $4, $5]) \bind 1 2 3 4 5
|
||||||
|
;
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
-- neither are prepared statements
|
||||||
|
-- the IN and ARRAY forms of this statement will have the same queryId
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
PREPARE p1(int, int, int, int, int) AS
|
||||||
|
SELECT * FROM test_squash WHERE id IN ($1, $2, $3, $4, $5);
|
||||||
|
EXECUTE p1(1, 2, 3, 4, 5);
|
||||||
|
DEALLOCATE p1;
|
||||||
|
PREPARE p1(int, int, int, int, int) AS
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY[$1, $2, $3, $4, $5]);
|
||||||
|
EXECUTE p1(1, 2, 3, 4, 5);
|
||||||
|
DEALLOCATE p1;
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- More conditions in the query
|
-- More conditions in the query
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9) AND data = 2;
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9) AND data = 2;
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND data = 2;
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) AND data = 2;
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) AND data = 2;
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) AND data = 2;
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]) AND data = 2;
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) AND data = 2;
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) AND data = 2;
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Multiple squashed intervals
|
-- Multiple squashed intervals
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9)
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9)
|
||||||
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
|
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||||
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||||
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
SELECT * FROM test_squash WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
||||||
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
AND data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
|
||||||
|
AND data = ANY (ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- No constants simplification for OpExpr
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
|
||||||
-- In the following two queries the operator expressions (+) and (@) have
|
-- No constants squashing for OpExpr
|
||||||
-- different oppno, and will be given different query_id if squashed, even though
|
-- The IN and ARRAY forms of this statement will have the same queryId
|
||||||
-- the normalized query will be the same
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash WHERE id IN
|
SELECT * FROM test_squash WHERE id IN
|
||||||
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
|
(1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9);
|
||||||
SELECT * FROM test_squash WHERE id IN
|
SELECT * FROM test_squash WHERE id IN
|
||||||
(@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9');
|
(@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9');
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY
|
||||||
|
[1 + 1, 2 + 2, 3 + 3, 4 + 4, 5 + 5, 6 + 6, 7 + 7, 8 + 8, 9 + 9]);
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY
|
||||||
|
[@ '-1', @ '-2', @ '-3', @ '-4', @ '-5', @ '-6', @ '-7', @ '-8', @ '-9']);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
--
|
||||||
-- FuncExpr
|
-- FuncExpr
|
||||||
|
--
|
||||||
|
|
||||||
-- Verify multiple type representation end up with the same query_id
|
-- Verify multiple type representation end up with the same query_id
|
||||||
CREATE TABLE test_float (data float);
|
CREATE TABLE test_float (data float);
|
||||||
|
-- The casted ARRAY expressions will have the same queryId as the IN clause
|
||||||
|
-- form of the query
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT data FROM test_float WHERE data IN (1, 2);
|
SELECT data FROM test_float WHERE data IN (1, 2);
|
||||||
SELECT data FROM test_float WHERE data IN (1, '2');
|
SELECT data FROM test_float WHERE data IN (1, '2');
|
||||||
SELECT data FROM test_float WHERE data IN ('1', 2);
|
SELECT data FROM test_float WHERE data IN ('1', 2);
|
||||||
SELECT data FROM test_float WHERE data IN ('1', '2');
|
SELECT data FROM test_float WHERE data IN ('1', '2');
|
||||||
SELECT data FROM test_float WHERE data IN (1.0, 1.0);
|
SELECT data FROM test_float WHERE data IN (1.0, 1.0);
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY['1'::double precision, '2'::double precision]);
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1.0::double precision, 1.0::double precision]);
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, 2]);
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY[1, '2']);
|
||||||
|
SELECT data FROM test_float WHERE data = ANY(ARRAY['1', 2]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Numeric type, implicit cast is squashed
|
-- Numeric type, implicit cast is squashed
|
||||||
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
|
CREATE TABLE test_squash_numeric (id int, data numeric(5, 2));
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_numeric WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
SELECT * FROM test_squash_numeric WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||||
|
SELECT * FROM test_squash_numeric WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Bigint, implicit cast is squashed
|
-- Bigint, implicit cast is squashed
|
||||||
CREATE TABLE test_squash_bigint (id int, data bigint);
|
CREATE TABLE test_squash_bigint (id int, data bigint);
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_bigint WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
SELECT * FROM test_squash_bigint WHERE data IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||||
|
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Bigint, explicit cast is not squashed
|
-- Bigint, explicit cast is squashed
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_bigint WHERE data IN
|
SELECT * FROM test_squash_bigint WHERE data IN
|
||||||
(1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
|
(1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
|
||||||
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint);
|
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint);
|
||||||
|
SELECT * FROM test_squash_bigint WHERE data = ANY(ARRAY[
|
||||||
|
1::bigint, 2::bigint, 3::bigint, 4::bigint, 5::bigint, 6::bigint,
|
||||||
|
7::bigint, 8::bigint, 9::bigint, 10::bigint, 11::bigint]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Bigint, long tokens with parenthesis
|
-- Bigint, long tokens with parenthesis, will not squash
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_bigint WHERE id IN
|
SELECT * FROM test_squash_bigint WHERE id IN
|
||||||
(abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
|
(abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
|
||||||
abs(800), abs(900), abs(1000), ((abs(1100))));
|
abs(800), abs(900), abs(1000), ((abs(1100))));
|
||||||
|
SELECT * FROM test_squash_bigint WHERE id = ANY(ARRAY[
|
||||||
|
abs(100), abs(200), abs(300), abs(400), abs(500), abs(600), abs(700),
|
||||||
|
abs(800), abs(900), abs(1000), ((abs(1100)))]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- CoerceViaIO, SubLink instead of a Const
|
-- Multiple FuncExpr's. Will not squash
|
||||||
CREATE TABLE test_squash_jsonb (id int, data jsonb);
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN
|
SELECT WHERE 1 IN (1::int::bigint::int, 2::int::bigint::int);
|
||||||
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
SELECT WHERE 1 = ANY(ARRAY[1::int::bigint::int, 2::int::bigint::int]);
|
||||||
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
|
||||||
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
|
||||||
(SELECT '"10"')::jsonb);
|
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
--
|
||||||
-- CoerceViaIO
|
-- CoerceViaIO
|
||||||
|
--
|
||||||
|
|
||||||
-- Create some dummy type to force CoerceViaIO
|
-- Create some dummy type to force CoerceViaIO
|
||||||
CREATE TYPE casttesttype;
|
CREATE TYPE casttesttype;
|
||||||
@ -141,19 +200,73 @@ SELECT * FROM test_squash_cast WHERE data IN
|
|||||||
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
|
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
|
||||||
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
|
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
|
||||||
10::int4::casttesttype, 11::int4::casttesttype);
|
10::int4::casttesttype, 11::int4::casttesttype);
|
||||||
|
SELECT * FROM test_squash_cast WHERE data = ANY (ARRAY
|
||||||
|
[1::int4::casttesttype, 2::int4::casttesttype, 3::int4::casttesttype,
|
||||||
|
4::int4::casttesttype, 5::int4::casttesttype, 6::int4::casttesttype,
|
||||||
|
7::int4::casttesttype, 8::int4::casttesttype, 9::int4::casttesttype,
|
||||||
|
10::int4::casttesttype, 11::int4::casttesttype]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Some casting expression are simplified to Const
|
-- Some casting expression are simplified to Const
|
||||||
|
CREATE TABLE test_squash_jsonb (id int, data jsonb);
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash_jsonb WHERE data IN
|
SELECT * FROM test_squash_jsonb WHERE data IN
|
||||||
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
(('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
||||||
( '"5"')::jsonb, ( '"6"')::jsonb, ( '"7"')::jsonb, ( '"8"')::jsonb,
|
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
|
||||||
( '"9"')::jsonb, ( '"10"')::jsonb);
|
('"9"')::jsonb, ('"10"')::jsonb);
|
||||||
|
SELECT * FROM test_squash_jsonb WHERE data = ANY (ARRAY
|
||||||
|
[('"1"')::jsonb, ('"2"')::jsonb, ('"3"')::jsonb, ('"4"')::jsonb,
|
||||||
|
('"5"')::jsonb, ('"6"')::jsonb, ('"7"')::jsonb, ('"8"')::jsonb,
|
||||||
|
('"9"')::jsonb, ('"10"')::jsonb]);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- RelabelType
|
-- CoerceViaIO, SubLink instead of a Const. Will not squash
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
|
SELECT * FROM test_squash_jsonb WHERE data IN
|
||||||
|
((SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
||||||
|
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
||||||
|
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
||||||
|
(SELECT '"10"')::jsonb);
|
||||||
|
SELECT * FROM test_squash_jsonb WHERE data = ANY(ARRAY
|
||||||
|
[(SELECT '"1"')::jsonb, (SELECT '"2"')::jsonb, (SELECT '"3"')::jsonb,
|
||||||
|
(SELECT '"4"')::jsonb, (SELECT '"5"')::jsonb, (SELECT '"6"')::jsonb,
|
||||||
|
(SELECT '"7"')::jsonb, (SELECT '"8"')::jsonb, (SELECT '"9"')::jsonb,
|
||||||
|
(SELECT '"10"')::jsonb]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
-- Multiple CoerceViaIO wrapping a constant. Will not squash
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
SELECT WHERE 1 IN (1::text::int::text::int, 1::text::int::text::int);
|
||||||
|
SELECT WHERE 1 = ANY(ARRAY[1::text::int::text::int, 1::text::int::text::int]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
--
|
||||||
|
-- RelabelType
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
-- if there is only one level of RelabelType, the list will be squashable
|
||||||
|
SELECT * FROM test_squash WHERE id IN
|
||||||
|
(1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid);
|
||||||
|
SELECT ARRAY[1::oid, 2::oid, 3::oid, 4::oid, 5::oid, 6::oid, 7::oid, 8::oid, 9::oid];
|
||||||
|
-- if there is at least one element with multiple levels of RelabelType,
|
||||||
|
-- the list will not be squashable
|
||||||
|
SELECT * FROM test_squash WHERE id IN (1::oid, 2::oid::int::oid);
|
||||||
|
SELECT * FROM test_squash WHERE id = ANY(ARRAY[1::oid, 2::oid::int::oid]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
--
|
||||||
|
-- edge cases
|
||||||
|
--
|
||||||
|
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
-- for nested arrays, only constants are squashed
|
||||||
|
SELECT ARRAY[
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
||||||
|
ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
];
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
-- Test constants evaluation in a CTE, which was causing issues in the past
|
-- Test constants evaluation in a CTE, which was causing issues in the past
|
||||||
@ -163,7 +276,26 @@ WITH cte AS (
|
|||||||
SELECT ARRAY['a', 'b', 'c', const::varchar] AS result
|
SELECT ARRAY['a', 'b', 'c', const::varchar] AS result
|
||||||
FROM cte;
|
FROM cte;
|
||||||
|
|
||||||
-- Simple array would be squashed as well
|
|
||||||
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
SELECT ARRAY[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
-- Rewritten as an OpExpr, so it will not be squashed
|
||||||
|
select where '1' IN ('1'::int, '2'::int::text);
|
||||||
|
-- Rewritten as an ArrayExpr, so it will be squashed
|
||||||
|
select where '1' IN ('1'::int, '2'::int);
|
||||||
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
SELECT pg_stat_statements_reset() IS NOT NULL AS t;
|
||||||
|
-- Both of these queries will be rewritten as an ArrayExpr, so they
|
||||||
|
-- will be squashed, and have a similar queryId
|
||||||
|
select where '1' IN ('1'::int::text, '2'::int::text);
|
||||||
|
select where '1' = ANY (array['1'::int::text, '2'::int::text]);
|
||||||
|
SELECT query, calls FROM pg_stat_statements ORDER BY query COLLATE "C";
|
||||||
|
|
||||||
|
--
|
||||||
|
-- cleanup
|
||||||
|
--
|
||||||
|
DROP TABLE test_squash;
|
||||||
|
DROP TABLE test_float;
|
||||||
|
DROP TABLE test_squash_numeric;
|
||||||
|
DROP TABLE test_squash_bigint;
|
||||||
|
DROP TABLE test_squash_cast CASCADE;
|
||||||
|
DROP TABLE test_squash_jsonb;
|
||||||
|
@ -1329,7 +1329,7 @@ _jumble${n}(JumbleState *jstate, Node *node)
|
|||||||
# Node type. Squash constants if requested.
|
# Node type. Squash constants if requested.
|
||||||
if ($query_jumble_squash)
|
if ($query_jumble_squash)
|
||||||
{
|
{
|
||||||
print $jff "\tJUMBLE_ELEMENTS($f);\n"
|
print $jff "\tJUMBLE_ELEMENTS($f, node);\n"
|
||||||
unless $query_jumble_ignore;
|
unless $query_jumble_ignore;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -653,6 +653,8 @@ _outA_Expr(StringInfo str, const A_Expr *node)
|
|||||||
|
|
||||||
WRITE_NODE_FIELD(lexpr);
|
WRITE_NODE_FIELD(lexpr);
|
||||||
WRITE_NODE_FIELD(rexpr);
|
WRITE_NODE_FIELD(rexpr);
|
||||||
|
WRITE_LOCATION_FIELD(rexpr_list_start);
|
||||||
|
WRITE_LOCATION_FIELD(rexpr_list_end);
|
||||||
WRITE_LOCATION_FIELD(location);
|
WRITE_LOCATION_FIELD(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,9 @@ static void AppendJumble(JumbleState *jstate,
|
|||||||
const unsigned char *value, Size size);
|
const unsigned char *value, Size size);
|
||||||
static void FlushPendingNulls(JumbleState *jstate);
|
static void FlushPendingNulls(JumbleState *jstate);
|
||||||
static void RecordConstLocation(JumbleState *jstate,
|
static void RecordConstLocation(JumbleState *jstate,
|
||||||
int location, bool squashed);
|
int location, int len);
|
||||||
static void _jumbleNode(JumbleState *jstate, Node *node);
|
static void _jumbleNode(JumbleState *jstate, Node *node);
|
||||||
static void _jumbleElements(JumbleState *jstate, List *elements);
|
static void _jumbleElements(JumbleState *jstate, List *elements, Node *node);
|
||||||
static void _jumbleA_Const(JumbleState *jstate, Node *node);
|
static void _jumbleA_Const(JumbleState *jstate, Node *node);
|
||||||
static void _jumbleList(JumbleState *jstate, Node *node);
|
static void _jumbleList(JumbleState *jstate, Node *node);
|
||||||
static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
|
static void _jumbleVariableSetStmt(JumbleState *jstate, Node *node);
|
||||||
@ -373,15 +373,17 @@ FlushPendingNulls(JumbleState *jstate)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Record location of constant within query string of query tree that is
|
* Record the location of some kind of constant within a query string.
|
||||||
* currently being walked.
|
* These are not only bare constants but also expressions that ultimately
|
||||||
|
* constitute a constant, such as those inside casts and simple function
|
||||||
|
* calls.
|
||||||
*
|
*
|
||||||
* 'squashed' signals that the constant represents the first or the last
|
* If length is -1, it indicates a single such constant element. If
|
||||||
* element in a series of merged constants, and everything but the first/last
|
* it's a positive integer, it indicates the length of a squashable
|
||||||
* element contributes nothing to the jumble hash.
|
* list of them.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
RecordConstLocation(JumbleState *jstate, int location, bool squashed)
|
RecordConstLocation(JumbleState *jstate, int location, int len)
|
||||||
{
|
{
|
||||||
/* -1 indicates unknown or undefined location */
|
/* -1 indicates unknown or undefined location */
|
||||||
if (location >= 0)
|
if (location >= 0)
|
||||||
@ -396,9 +398,14 @@ RecordConstLocation(JumbleState *jstate, int location, bool squashed)
|
|||||||
sizeof(LocationLen));
|
sizeof(LocationLen));
|
||||||
}
|
}
|
||||||
jstate->clocations[jstate->clocations_count].location = location;
|
jstate->clocations[jstate->clocations_count].location = location;
|
||||||
/* initialize lengths to -1 to simplify third-party module usage */
|
|
||||||
jstate->clocations[jstate->clocations_count].squashed = squashed;
|
/*
|
||||||
jstate->clocations[jstate->clocations_count].length = -1;
|
* Lengths are either positive integers (indicating a squashable
|
||||||
|
* list), or -1.
|
||||||
|
*/
|
||||||
|
Assert(len > -1 || len == -1);
|
||||||
|
jstate->clocations[jstate->clocations_count].length = len;
|
||||||
|
jstate->clocations[jstate->clocations_count].squashed = (len > -1);
|
||||||
jstate->clocations_count++;
|
jstate->clocations_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -408,12 +415,12 @@ RecordConstLocation(JumbleState *jstate, int location, bool squashed)
|
|||||||
* deduce that the expression is a constant:
|
* deduce that the expression is a constant:
|
||||||
*
|
*
|
||||||
* - Ignore a possible wrapping RelabelType and CoerceViaIO.
|
* - Ignore a possible wrapping RelabelType and CoerceViaIO.
|
||||||
* - If it's a FuncExpr, check that the function is an implicit
|
* - If it's a FuncExpr, check that the function is a builtin
|
||||||
* cast and its arguments are Const.
|
* cast and its arguments are Const.
|
||||||
* - Otherwise test if the expression is a simple Const.
|
* - Otherwise test if the expression is a simple Const.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
IsSquashableConst(Node *element)
|
IsSquashableConstant(Node *element)
|
||||||
{
|
{
|
||||||
if (IsA(element, RelabelType))
|
if (IsA(element, RelabelType))
|
||||||
element = (Node *) ((RelabelType *) element)->arg;
|
element = (Node *) ((RelabelType *) element)->arg;
|
||||||
@ -421,7 +428,9 @@ IsSquashableConst(Node *element)
|
|||||||
if (IsA(element, CoerceViaIO))
|
if (IsA(element, CoerceViaIO))
|
||||||
element = (Node *) ((CoerceViaIO *) element)->arg;
|
element = (Node *) ((CoerceViaIO *) element)->arg;
|
||||||
|
|
||||||
if (IsA(element, FuncExpr))
|
switch (nodeTag(element))
|
||||||
|
{
|
||||||
|
case T_FuncExpr:
|
||||||
{
|
{
|
||||||
FuncExpr *func = (FuncExpr *) element;
|
FuncExpr *func = (FuncExpr *) element;
|
||||||
ListCell *temp;
|
ListCell *temp;
|
||||||
@ -433,19 +442,35 @@ IsSquashableConst(Node *element)
|
|||||||
if (func->funcid > FirstGenbkiObjectId)
|
if (func->funcid > FirstGenbkiObjectId)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can check function arguments recursively, being careful
|
||||||
|
* about recursing too deep. At each recursion level it's
|
||||||
|
* enough to test the stack on the first element. (Note that
|
||||||
|
* I wasn't able to hit this without bloating the stack
|
||||||
|
* artificially in this function: the parser errors out before
|
||||||
|
* stack size becomes a problem here.)
|
||||||
|
*/
|
||||||
foreach(temp, func->args)
|
foreach(temp, func->args)
|
||||||
{
|
{
|
||||||
Node *arg = lfirst(temp);
|
Node *arg = lfirst(temp);
|
||||||
|
|
||||||
if (!IsA(arg, Const)) /* XXX we could recurse here instead */
|
if (!IsA(arg, Const))
|
||||||
|
{
|
||||||
|
if (foreach_current_index(temp) == 0 &&
|
||||||
|
stack_is_too_deep())
|
||||||
return false;
|
return false;
|
||||||
|
else if (!IsSquashableConstant(arg))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
if (!IsA(element, Const))
|
if (!IsA(element, Const))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -461,35 +486,29 @@ IsSquashableConst(Node *element)
|
|||||||
* expressions.
|
* expressions.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
IsSquashableConstList(List *elements, Node **firstExpr, Node **lastExpr)
|
IsSquashableConstantList(List *elements)
|
||||||
{
|
{
|
||||||
ListCell *temp;
|
ListCell *temp;
|
||||||
|
|
||||||
/*
|
/* If the list is too short, we don't try to squash it. */
|
||||||
* If squashing is disabled, or the list is too short, we don't try to
|
|
||||||
* squash it.
|
|
||||||
*/
|
|
||||||
if (list_length(elements) < 2)
|
if (list_length(elements) < 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach(temp, elements)
|
foreach(temp, elements)
|
||||||
{
|
{
|
||||||
if (!IsSquashableConst(lfirst(temp)))
|
if (!IsSquashableConstant(lfirst(temp)))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
*firstExpr = linitial(elements);
|
|
||||||
*lastExpr = llast(elements);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define JUMBLE_NODE(item) \
|
#define JUMBLE_NODE(item) \
|
||||||
_jumbleNode(jstate, (Node *) expr->item)
|
_jumbleNode(jstate, (Node *) expr->item)
|
||||||
#define JUMBLE_ELEMENTS(list) \
|
#define JUMBLE_ELEMENTS(list, node) \
|
||||||
_jumbleElements(jstate, (List *) expr->list)
|
_jumbleElements(jstate, (List *) expr->list, node)
|
||||||
#define JUMBLE_LOCATION(location) \
|
#define JUMBLE_LOCATION(location) \
|
||||||
RecordConstLocation(jstate, expr->location, false)
|
RecordConstLocation(jstate, expr->location, -1)
|
||||||
#define JUMBLE_FIELD(item) \
|
#define JUMBLE_FIELD(item) \
|
||||||
do { \
|
do { \
|
||||||
if (sizeof(expr->item) == 8) \
|
if (sizeof(expr->item) == 8) \
|
||||||
@ -517,36 +536,36 @@ do { \
|
|||||||
#include "queryjumblefuncs.funcs.c"
|
#include "queryjumblefuncs.funcs.c"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We jumble lists of constant elements as one individual item regardless
|
* We try to jumble lists of expressions as one individual item regardless
|
||||||
* of how many elements are in the list. This means different queries
|
* of how many elements are in the list. This is know as squashing, which
|
||||||
* jumble to the same query_id, if the only difference is the number of
|
* results in different queries jumbling to the same query_id, if the only
|
||||||
* elements in the list.
|
* difference is the number of elements in the list.
|
||||||
|
*
|
||||||
|
* We allow constants to be squashed. To normalize such queries, we use
|
||||||
|
* the start and end locations of the list of elements in a list.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
_jumbleElements(JumbleState *jstate, List *elements)
|
_jumbleElements(JumbleState *jstate, List *elements, Node *node)
|
||||||
{
|
{
|
||||||
Node *first,
|
bool normalize_list = false;
|
||||||
*last;
|
|
||||||
|
|
||||||
if (IsSquashableConstList(elements, &first, &last))
|
if (IsSquashableConstantList(elements))
|
||||||
{
|
{
|
||||||
/*
|
if (IsA(node, ArrayExpr))
|
||||||
* If this list of elements is squashable, keep track of the location
|
{
|
||||||
* of its first and last elements. When reading back the locations
|
ArrayExpr *aexpr = (ArrayExpr *) node;
|
||||||
* array, we'll see two consecutive locations with ->squashed set to
|
|
||||||
* true, indicating the location of initial and final elements of this
|
if (aexpr->list_start > 0 && aexpr->list_end > 0)
|
||||||
* list.
|
{
|
||||||
*
|
RecordConstLocation(jstate,
|
||||||
* For the limited set of cases we support now (implicit coerce via
|
aexpr->list_start + 1,
|
||||||
* FuncExpr, Const) it's fine to use exprLocation of the 'last'
|
(aexpr->list_end - aexpr->list_start) - 1);
|
||||||
* expression, but if more complex composite expressions are to be
|
normalize_list = true;
|
||||||
* supported (e.g., OpExpr or FuncExpr as an explicit call), more
|
|
||||||
* sophisticated tracking will be needed.
|
|
||||||
*/
|
|
||||||
RecordConstLocation(jstate, exprLocation(first), true);
|
|
||||||
RecordConstLocation(jstate, exprLocation(last), true);
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!normalize_list)
|
||||||
{
|
{
|
||||||
_jumbleNode(jstate, (Node *) elements);
|
_jumbleNode(jstate, (Node *) elements);
|
||||||
}
|
}
|
||||||
|
@ -526,6 +526,8 @@ _readA_Expr(void)
|
|||||||
|
|
||||||
READ_NODE_FIELD(lexpr);
|
READ_NODE_FIELD(lexpr);
|
||||||
READ_NODE_FIELD(rexpr);
|
READ_NODE_FIELD(rexpr);
|
||||||
|
READ_LOCATION_FIELD(rexpr_list_start);
|
||||||
|
READ_LOCATION_FIELD(rexpr_list_end);
|
||||||
READ_LOCATION_FIELD(location);
|
READ_LOCATION_FIELD(location);
|
||||||
|
|
||||||
READ_DONE();
|
READ_DONE();
|
||||||
|
@ -183,7 +183,7 @@ static void doNegateFloat(Float *v);
|
|||||||
static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location);
|
static Node *makeAndExpr(Node *lexpr, Node *rexpr, int location);
|
||||||
static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location);
|
static Node *makeOrExpr(Node *lexpr, Node *rexpr, int location);
|
||||||
static Node *makeNotExpr(Node *expr, int location);
|
static Node *makeNotExpr(Node *expr, int location);
|
||||||
static Node *makeAArrayExpr(List *elements, int location);
|
static Node *makeAArrayExpr(List *elements, int location, int end_location);
|
||||||
static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
|
static Node *makeSQLValueFunction(SQLValueFunctionOp op, int32 typmod,
|
||||||
int location);
|
int location);
|
||||||
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
|
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
|
||||||
@ -522,7 +522,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
|
%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
|
||||||
%type <node> def_arg columnElem where_clause where_or_current_clause
|
%type <node> def_arg columnElem where_clause where_or_current_clause
|
||||||
a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
|
a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
|
||||||
columnref in_expr having_clause func_table xmltable array_expr
|
columnref having_clause func_table xmltable array_expr
|
||||||
OptWhereClause operator_def_arg
|
OptWhereClause operator_def_arg
|
||||||
%type <list> opt_column_and_period_list
|
%type <list> opt_column_and_period_list
|
||||||
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
|
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
|
||||||
@ -15264,14 +15264,12 @@ a_expr: c_expr { $$ = $1; }
|
|||||||
(Node *) list_make2($5, $7),
|
(Node *) list_make2($5, $7),
|
||||||
@2);
|
@2);
|
||||||
}
|
}
|
||||||
| a_expr IN_P in_expr
|
| a_expr IN_P select_with_parens
|
||||||
{
|
|
||||||
/* in_expr returns a SubLink or a list of a_exprs */
|
|
||||||
if (IsA($3, SubLink))
|
|
||||||
{
|
{
|
||||||
/* generate foo = ANY (subquery) */
|
/* generate foo = ANY (subquery) */
|
||||||
SubLink *n = (SubLink *) $3;
|
SubLink *n = makeNode(SubLink);
|
||||||
|
|
||||||
|
n->subselect = $3;
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
n->subLinkId = 0;
|
n->subLinkId = 0;
|
||||||
n->testexpr = $1;
|
n->testexpr = $1;
|
||||||
@ -15279,21 +15277,21 @@ a_expr: c_expr { $$ = $1; }
|
|||||||
n->location = @2;
|
n->location = @2;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
else
|
| a_expr IN_P '(' expr_list ')'
|
||||||
{
|
{
|
||||||
/* generate scalar IN expression */
|
/* generate scalar IN expression */
|
||||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2);
|
A_Expr *n = makeSimpleA_Expr(AEXPR_IN, "=", $1, (Node *) $4, @2);
|
||||||
|
|
||||||
|
n->rexpr_list_start = @3;
|
||||||
|
n->rexpr_list_end = @5;
|
||||||
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
}
|
| a_expr NOT_LA IN_P select_with_parens %prec NOT_LA
|
||||||
| a_expr NOT_LA IN_P in_expr %prec NOT_LA
|
|
||||||
{
|
|
||||||
/* in_expr returns a SubLink or a list of a_exprs */
|
|
||||||
if (IsA($4, SubLink))
|
|
||||||
{
|
{
|
||||||
/* generate NOT (foo = ANY (subquery)) */
|
/* generate NOT (foo = ANY (subquery)) */
|
||||||
/* Make an = ANY node */
|
SubLink *n = makeNode(SubLink);
|
||||||
SubLink *n = (SubLink *) $4;
|
|
||||||
|
|
||||||
|
n->subselect = $4;
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
n->subLinkId = 0;
|
n->subLinkId = 0;
|
||||||
n->testexpr = $1;
|
n->testexpr = $1;
|
||||||
@ -15302,11 +15300,14 @@ a_expr: c_expr { $$ = $1; }
|
|||||||
/* Stick a NOT on top; must have same parse location */
|
/* Stick a NOT on top; must have same parse location */
|
||||||
$$ = makeNotExpr((Node *) n, @2);
|
$$ = makeNotExpr((Node *) n, @2);
|
||||||
}
|
}
|
||||||
else
|
| a_expr NOT_LA IN_P '(' expr_list ')'
|
||||||
{
|
{
|
||||||
/* generate scalar NOT IN expression */
|
/* generate scalar NOT IN expression */
|
||||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "<>", $1, $4, @2);
|
A_Expr *n = makeSimpleA_Expr(AEXPR_IN, "<>", $1, (Node *) $5, @2);
|
||||||
}
|
|
||||||
|
n->rexpr_list_start = @4;
|
||||||
|
n->rexpr_list_end = @6;
|
||||||
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| a_expr subquery_Op sub_type select_with_parens %prec Op
|
| a_expr subquery_Op sub_type select_with_parens %prec Op
|
||||||
{
|
{
|
||||||
@ -16741,15 +16742,15 @@ type_list: Typename { $$ = list_make1($1); }
|
|||||||
|
|
||||||
array_expr: '[' expr_list ']'
|
array_expr: '[' expr_list ']'
|
||||||
{
|
{
|
||||||
$$ = makeAArrayExpr($2, @1);
|
$$ = makeAArrayExpr($2, @1, @3);
|
||||||
}
|
}
|
||||||
| '[' array_expr_list ']'
|
| '[' array_expr_list ']'
|
||||||
{
|
{
|
||||||
$$ = makeAArrayExpr($2, @1);
|
$$ = makeAArrayExpr($2, @1, @3);
|
||||||
}
|
}
|
||||||
| '[' ']'
|
| '[' ']'
|
||||||
{
|
{
|
||||||
$$ = makeAArrayExpr(NIL, @1);
|
$$ = makeAArrayExpr(NIL, @1, @2);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -16871,17 +16872,6 @@ trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); }
|
|||||||
| expr_list { $$ = $1; }
|
| expr_list { $$ = $1; }
|
||||||
;
|
;
|
||||||
|
|
||||||
in_expr: select_with_parens
|
|
||||||
{
|
|
||||||
SubLink *n = makeNode(SubLink);
|
|
||||||
|
|
||||||
n->subselect = $1;
|
|
||||||
/* other fields will be filled later */
|
|
||||||
$$ = (Node *) n;
|
|
||||||
}
|
|
||||||
| '(' expr_list ')' { $$ = (Node *) $2; }
|
|
||||||
;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define SQL-style CASE clause.
|
* Define SQL-style CASE clause.
|
||||||
* - Full specification
|
* - Full specification
|
||||||
@ -19232,12 +19222,14 @@ makeNotExpr(Node *expr, int location)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Node *
|
static Node *
|
||||||
makeAArrayExpr(List *elements, int location)
|
makeAArrayExpr(List *elements, int location, int location_end)
|
||||||
{
|
{
|
||||||
A_ArrayExpr *n = makeNode(A_ArrayExpr);
|
A_ArrayExpr *n = makeNode(A_ArrayExpr);
|
||||||
|
|
||||||
n->elements = elements;
|
n->elements = elements;
|
||||||
n->location = location;
|
n->location = location;
|
||||||
|
n->list_start = location;
|
||||||
|
n->list_end = location_end;
|
||||||
return (Node *) n;
|
return (Node *) n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,6 +1223,8 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
|
|||||||
newa->element_typeid = scalar_type;
|
newa->element_typeid = scalar_type;
|
||||||
newa->elements = aexprs;
|
newa->elements = aexprs;
|
||||||
newa->multidims = false;
|
newa->multidims = false;
|
||||||
|
newa->list_start = a->rexpr_list_start;
|
||||||
|
newa->list_end = a->rexpr_list_end;
|
||||||
newa->location = -1;
|
newa->location = -1;
|
||||||
|
|
||||||
result = (Node *) make_scalar_array_op(pstate,
|
result = (Node *) make_scalar_array_op(pstate,
|
||||||
@ -2165,6 +2167,8 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a,
|
|||||||
/* array_collid will be set by parse_collate.c */
|
/* array_collid will be set by parse_collate.c */
|
||||||
newa->element_typeid = element_type;
|
newa->element_typeid = element_type;
|
||||||
newa->elements = newcoercedelems;
|
newa->elements = newcoercedelems;
|
||||||
|
newa->list_start = a->list_start;
|
||||||
|
newa->list_end = a->list_end;
|
||||||
newa->location = a->location;
|
newa->location = a->location;
|
||||||
|
|
||||||
return (Node *) newa;
|
return (Node *) newa;
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202506021
|
#define CATALOG_VERSION_NO 202506121
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -351,6 +351,14 @@ typedef struct A_Expr
|
|||||||
List *name; /* possibly-qualified name of operator */
|
List *name; /* possibly-qualified name of operator */
|
||||||
Node *lexpr; /* left argument, or NULL if none */
|
Node *lexpr; /* left argument, or NULL if none */
|
||||||
Node *rexpr; /* right argument, or NULL if none */
|
Node *rexpr; /* right argument, or NULL if none */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If rexpr is a list of some kind, we separately track its starting and
|
||||||
|
* ending location; it's not the same as the starting and ending location
|
||||||
|
* of the token itself.
|
||||||
|
*/
|
||||||
|
ParseLoc rexpr_list_start;
|
||||||
|
ParseLoc rexpr_list_end;
|
||||||
ParseLoc location; /* token location, or -1 if unknown */
|
ParseLoc location; /* token location, or -1 if unknown */
|
||||||
} A_Expr;
|
} A_Expr;
|
||||||
|
|
||||||
@ -506,6 +514,8 @@ typedef struct A_ArrayExpr
|
|||||||
{
|
{
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
List *elements; /* array element expressions */
|
List *elements; /* array element expressions */
|
||||||
|
ParseLoc list_start; /* start of the element list */
|
||||||
|
ParseLoc list_end; /* end of the elements list */
|
||||||
ParseLoc location; /* token location, or -1 if unknown */
|
ParseLoc location; /* token location, or -1 if unknown */
|
||||||
} A_ArrayExpr;
|
} A_ArrayExpr;
|
||||||
|
|
||||||
|
@ -1397,6 +1397,10 @@ typedef struct ArrayExpr
|
|||||||
List *elements pg_node_attr(query_jumble_squash);
|
List *elements pg_node_attr(query_jumble_squash);
|
||||||
/* true if elements are sub-arrays */
|
/* true if elements are sub-arrays */
|
||||||
bool multidims pg_node_attr(query_jumble_ignore);
|
bool multidims pg_node_attr(query_jumble_ignore);
|
||||||
|
/* location of the start of the elements list */
|
||||||
|
ParseLoc list_start;
|
||||||
|
/* location of the end of the elements list */
|
||||||
|
ParseLoc list_end;
|
||||||
/* token location, or -1 if unknown */
|
/* token location, or -1 if unknown */
|
||||||
ParseLoc location;
|
ParseLoc location;
|
||||||
} ArrayExpr;
|
} ArrayExpr;
|
||||||
|
Reference in New Issue
Block a user