This section explains new features related to SQL statements.
3.2.1 ALTER DEFAULT PRIVILEGE
Default permissions can now be set for large objects. The defaclobjtype column in the pg_default_acl catalog will output the L indicating a large object. [0d6c477]
Example 31Default privileges for LARGE OBJECT
postgres=> ALTER DEFAULT PRIVILEGES GRANT SELECT ON LARGE OBJECTS TO PUBLIC ;
ALTER DEFAULT PRIVILEGES
3.2.2 ALTER SUBSCRIPTION
The two_phase option can now be altered in the ALTER SUBSCRIPTION statement; the SUBSCRIPTION object must be disabled once in order to alter the two_phase option. [1462aad]
Example 32Modify the two_phase option
postgres=# SELECT subname, subtwophasestate FROM pg_subscription ;
subname | subtwophasestate
---------+------------------
sub1 | e
(1 row)
postgres=# ALTER SUBSCRIPTION sub1 DISABLE ;
ALTER SUBSCRIPTION
postgres=# ALTER SUBSCRIPTION sub1 SET (two_phase=off) ;
ALTER SUBSCRIPTION
postgres=# SELECT subname, subtwophasestate FROM pg_subscription ;
subname | subtwophasestate
---------+------------------
sub1 | d
(1 row)
PDF page 35
3.2.3 ALTER TABLE
The following syntax has been added to the ALTER TABLE statement.
ALTER TABLE ALTER CONSTRAINT
The SET INHERIT clause (or SET NO INHERIT clause) can be specified in the ALTER CONSTRAINT clause. [f4e53e1]
Example 33Specifying the SET INHERIT clause
postgres=> CREATE TABLE data1(col1 INT PRIMARY KEY, col2 VARCHAR(10) NOT NULL) ;
CREATE TABLE
postgres=> ALTER TABLE data1 ALTER CONSTRAINT data1_col2_not_null NO INHERIT ;
ALTER TABLE
ALTER TABLE NOT NULL NOT VALID
The NOT VALID clause can be specified for NOT NULL constraints in an ALTER TABLE statement. [a379061]
Example 34Specifying the NOT VALID clause
postgres=> ALTER TABLE data1 ADD CONSTRAINT nn_data1_col2 NOT NULL
col2 NOT VALID ;
ALTER TABLE
postgres=> \d+ data1
Table "public.data1"
Column | Type | Collation | Nullable | Default | Storage …
--------+-----------------------+-----------+----------+---------+--------- …
col1 | integer | | not null | | plain …
col2 | character varying(10) | | not null | | extende …
Indexes:
"data1_pkey" PRIMARY KEY, btree (col1)
Not-null constraints:
"data1_col1_not_null" NOT NULL "col1"
"nn_data1_col2" NOT NULL "col2" NOT VALID
Access method: heap
PDF page 36
ALTER TABLE ONLY DROP CONSTRAINT
ALTER TABLE DROP CONSTRAINT statements with ONLY clause for partitioned tables can now be executed. In previous versions, this would have resulted in an error. [4dea33c]
Example 35Specifying the ONLY clause
postgres=> CREATE TABLE part1(c1 INT PRIMARY KEY, c2 INT CHECK(c2 > 0))
PARTITION BY RANGE(c1) ;
CREATE TABLE
postgres=> CREATE TABLE part1v1 PARTITION OF part1 FOR VALUES
FROM (0) TO (1000000) ;
CREATE TABLE
postgres=> ALTER TABLE ONLY part1 DROP CONSTRAINT part1_c2_check ;
ALTER TABLE
3.2.4 ANALYZE
The following new features have been implemented for the ANALYZE statement.
ONLY clause
In previous versions, executing an ANALYZE statement on a partition table would recursively collect statistics for the partition. By specifying the ONLY clause, statistics can be collected only for partition tables. [62ddf7e]
Example 36Specifying the ONLY clause
postgres=> ANALYZE VERBOSE ONLY part1 ;
INFO: analyzing "public.part1" inheritance tree
INFO: "part1v1": scanned 5406 of 5406 pages, containing 500000 live rows and
0 dead rows; 30000 rows in sample, 500000 estimated total rows
INFO: finished analyzing table "postgres.public.part1"
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
buffer usage: 5445 hits, 0 reads, 0 dirtied
WAL usage: 5 records, 0 full page images, 1195 bytes, 0 buffers full
system usage: CPU: user: 0.04 s, system: 0.00 s, elapsed: 0.04 s
ANALYZE
PDF page 37
Output of I/O statistics and WAL statistics
The output of the ANALYZE VERBOSE statement now includes I/O and WAL statistics. The I/O statistics output is similar to the logs for the parameter log_autovacuum_min_duration setting. [4c1b4cd, bb77752]
Example 37Output of the ANALYZE VERBOSE statement
postgres=> ANALYZE VERBOSE data1 ;
INFO: analyzing "public.data1"
INFO: "data1": scanned 5406 of 5406 pages, containing 333333 live rows and
666667 dead rows; 30000 rows in sample, 333333 estimated total rows
INFO: finished analyzing table "postgres.public.data1"
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
buffer usage: 5421 hits, 0 reads, 0 dirtied
WAL usage: 4 records, 0 full page images, 928 bytes, 0 buffers full
system usage: CPU: user: 0.02 s, system: 0.00 s, elapsed: 0.02 s
ANALYZE
Lines beginning with “INFO:” are I/O statistics and lines beginning with “WAL usage:” are WAL
statistics.
Output of delay time
If the parameter track_cost_delay_timing is set to ‘on’, the ANALYZE VERBOSE statement will now output “delay time”. The same information is also output in the log of automatic VACUUM. [7720082]
Example 38Output of the ANALYZE VERBOSE statement
postgres=> ANALYZE VERBOSE data1 ;
INFO: analyzing "public.data1"
INFO: "data1": scanned 5406 of 5406 pages, containing 500000 live rows and 0
dead rows; 30000 rows in sample, 500000 estimated total rows
INFO: finished analyzing table "postgres.public.data1"
delay time: 0.000 ms
I/O timings: read: 25.770 ms, write: 0.000 ms
…
WAL usage: 4 records, 1 full page images, 8240 bytes, 0 buffers full
system usage: CPU: user: 0.01 s, system: 0.03 s, elapsed: 0.05 s
ANALYZE
PDF page 38
Output of WAL buffer full
The WAL buffer full count is now output when the ANALYZE VERBOSE statement is executed. [6a8a7ce]
Example 39Output of the ANALYZE VERBOSE statement
postgres=> ANALYZE VERBOSE data1 ;
INFO: analyzing "public.data1"
INFO: "data1": scanned 5406 of 5406 pages, containing 500000 live rows and 0
dead rows; 30000 rows in sample, 500000 estimated total rows
INFO: finished analyzing table "postgres.public.data1"
avg read rate: 0.000 MB/s, avg write rate: 0.300 MB/s
buffer usage: 5425 hits, 0 reads, 1 dirtied
WAL usage: 3 records, 1 full page images, 3544 bytes, 0 buffers full
system usage: CPU: user: 0.02 s, system: 0.00 s, elapsed: 0.02 s
3.2.5 COPY
The following features have been added to the COPY statement
COPY TO statement from a materialized view
The COPY TO statement can now specify the materialized view. [534874f]
Example 40COPY TO statement for materialized view
postgres=> CREATE MATERIALIZED VIEW mview1 AS SELECT col1, col2 FROM data1 ;
SELECT 1000000
postgres=> COPY mview1 TO '/tmp/data1.csv' CSV ;
COPY 1000000
Addition of the reject_limit option
The COPY FROM statement now has a reject_limit option. This option allows the maximum number of errors to be specified when errors are ignored (on_error=ignore). If the number of errors exceeds the specified value, the COPY statement will fail. [4ac2a9b, a39297e]
PDF page 39
Example 41Specifying the REJECT_LIMIT option
postgres=> \! cat data1.csv
1,data1
2,data2
ABC,data3
4,data4
DEF,data5
postgres=> COPY data1 FROM '/home/postgres/data1.csv' WITH (FORMAT csv,
ON_ERROR ignore, REJECT_LIMIT 1, LOG_VERBOSITY verbose) ;
NOTICE: skipping row due to data type incompatibility at line 3 for column
"col1": "ABC"
NOTICE: skipping row due to data type incompatibility at line 5 for column
"col1": "DEF"
ERROR: skipped more than REJECT_LIMIT (1) rows due to data type incompatibility
CONTEXT: COPY data1, line 5, column col1: "DEF"
Added value for log_verbosity option
The log_verbosity option can now be ‘silent’ in the COPY statement. Specifying this value suppresses log output. [e7834a1
3.2.6 CREATE FOREIGN TABLE
The LIKE clause now allows copying column definitions from other tables (or foreign tables). [302cf157]
Example 42Specifying the LIKE Clause for External Tables
Table 17Values that can be specified in the INCLUDING / EXCLUDING clause
3.2.7 CREATE INDEX
Creation of GIN indexes is now processed in parallel with B-Tree and BRIN indexes. [8492feb] 8492feb
3.2.8 CREATE TABLE
The following enhancements have been implemented for the CREATE TABLE statement
WITHOUT OVERLAPS clause
It is now possible to specify a WITHOUT OVERLAPS clause that does not allow duplicate ranges for primary key constraints (PRIMARY KEY), unique constraints (UNIQUE) and foreign key constraints (FOREIGN KEY). At least two columns are required when specifying this clause for a primary key constraint. It can also be specified when adding a primary key in an ALTER TABLE statement. The column CONPERIOD has been added to the pg_constraint catalog to represent the
Example 43WITHOUT OVERLAPS clause in the primary key
postgres=> CREATE TABLE temporal_range1 (
id int4range,
valid_at daterange,
CONSTRAINT temporal_rng_pk PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
) ;
CREATE TABLE
postgres=> \d temporal_range1
Table "public.temporal_range1"
Column | Type | Collation | Nullable | Default
----------+-----------+-----------+----------+---------
id | int4range | | not null |
valid_at | daterange | | not null |
Indexes:
"temporal_rng_pk" PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
ENFORCED clause
Allows the ENFORCED or NOT ENFORCED clause to be specified for CHECK constraints and foreign key constraints. If nothing is specified or the ENFORCED clause is specified, PostgreSQL will behave as before, with CHECK constraints operating at the appropriate time. If the NOT ENFORCED clause is specified, the CHECK constraint will not work. The column CONFORCED has been added to the pg_constraint catalog to indicate this specification. [ca87c41, eec0040]
PDF page 42
Example 44CHECK constraints that do not operate
postgres=> CREATE TABLE data1(col1 INT PRIMARY KEY,
col2 INT CHECK (col2 < 10) NOT ENFORCED) ;
CREATE TABLE
postgres=> INSERT INTO data1 VALUES (100, 100) ;
INSERT 0 1
postgres=> SELECT * FROM data1 ;
col1 | col2
-------+-------
100 | 100
(1 row)
postgres=> SELECT conenforced FROM pg_constraint
WHERE conname='data1_col2_check' ;
conenforced
-------------
f
(1 row)
NOT VALID clause
Allows the NOT VALID clause to be specified as a foreign key for partition tables. [b663b94]
Example 45NOT VALID clause for foreign keys in partition tables
postgres=> CREATE TABLE fkdata1(col1 INT PRIMARY KEY, col2 VARCHAR(10)) ;
CREATE TABLE
postgres=> CREATE TABLE part1(col1 INT PRIMARY KEY, col2 VARCHAR(10))
PARTITION BY RANGE(col1) ;
CREATE TABLE
postgres=> ALTER TABLE part1 ADD FOREIGN KEY (col1) REFERENCES fkdata1
NOT VALID ;
ALTER TABLE
PDF page 43
GENERATED ALWAYS VIRTUAL clause
Allows the creation of auto-generated columns that do not hold the data. Specify a GENERATED ALWAYS VIRTUAL clause in the column definition. Previously, only GENERATED ALWAYS STORED clauses that retained actual data were supported. The VIRTUAL clause can be confirmed by the output of ‘v’ in the attgenerated column of the pg_attribute column. Indexes cannot be created on generated columns with VIRTUAL clause specified. [83ea6c5]
postgres=> CREATE TABLE overflow1(col1 VARCHAR(3), col2 VARCHAR(3)
GENERATED ALWAYS AS (col1 || 'X')) ;
CREATE TABLE
postgres=> INSERT INTO overflow1(col1) VALUES ('ABC') ;
INSERT 0 1
postgres=> SELECT * FROM overflow1 ;
ERROR: value too long for type character varying(3)
User-defined types and user-defined functions cannot be used for the virtual columns. [0cd69b3]
Example 48Using user-defined functions
postgres=> CREATE FUNCTION udf1(p1 INT) RETURNS INT AS
$$ SELECT p1*5 $$ IMMUTABLE LANGUAGE SQL ;
CREATE FUNCTION
postgres=> CREATE TABLE data1(c1 INT PRIMARY KEY,
c2 INT GENERATED ALWAYS AS (udf1(c1)) VIRTUAL) ;
ERROR: generation expression uses user-defined function
LINE 1: ...1(c1 INT PRIMARY KEY, c2 INT GENERATED ALWAYS AS (udf1(c1)) ...
^
DETAIL: Virtual generated columns that make use of user-defined functions are
not yet supported.
3.2.9 EXPLAIN
The following features have been added to the EXPLAIN statement.
Disabled Nodes Output
The output of the EXPLAIN statement now includes information on the number of disabled nodes (Disabled Nodes:). For example, disabling some execution plans by setting the parameter enable_seqscan to ‘false’, which was previously handled by increasing the cost, is now clearly recognized as a disabled node in PostgreSQL 18. [e222534, c01743a, 161320b]
PDF page 45
Example 49Disabling Nodes (PostgreSQL 18)
postgres=> SET enable_seqscan = off ;
SET
postgres=> EXPLAIN SELECT * FROM data1 ;
QUERY PLAN
----------------------------------------------------------------
Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=10)
Disabled: true
(2 rows)
Example 50Disabling Nodes (PostgreSQL 17)
postgres=> SET enable_seqscan = off ;
SET
postgres=> EXPLAIN SELECT * FROM data1 ;
QUERY PLAN
------------------------------------------------------------------------------
Seq Scan on data1 (cost=10000000000.00..10000015406.00 rows=1000000 width=10)
(1 row)
Example 51src/backend/optimizer/path/costsize.c(PostgreSQL 17)
Temporary storage information used during Window Aggregation and CTE Scan runs is now output as a Storage clause. [95d6e9a, 40708ac]
PDF page 46
Example 52Windows Aggregation temporary storage information
postgres=> EXPLAIN (ANALYZE, COSTS OFF, BUFFERS OFF)
SELECT COUNT(*) OVER() FROM data1 ;
QUERY PLAN
-----------------------------------------------------------------------------
WindowAgg (actual time=152.260..208.545 rows=1000000.00 loops=1)
Window: w1 AS ()
Storage: Disk Maximum Storage: 9766kB
-> Index Only Scan using data1_pkey on data1 (actual time=0.024..60.216 …
Heap Fetches: 0
Index Searches: 1
Planning Time: 0.059 ms
Execution Time: 224.929 ms
(8 rows)
Example 53CTE Scan Temporary Storage Information
postgres=> EXPLAIN (ANALYZE, COSTS OFF, BUFFERS OFF) WITH cte1 AS
MATERIALIZED (SELECT col1, col2 FROM data1) SELECT * FROM cte1 WHERE col1 <
10000 ;
QUERY PLAN
------------------------------------------------------------------------------
CTE Scan on cte1 (actual time=0.008..112.987 rows=9999.00 loops=1)
Filter: (col1 < 10000)
Rows Removed by Filter: 990001
Storage: Disk Maximum Storage: 19528kB
CTE cte1
-> Seq Scan on data1 (actual time=0.005..30.641 rows=1000000.00 loop …
Disabled: true
…
Parallel Bitmap Heap Scan Statistics
Statistics for Parallel Bitmap Heap Scan execution are now output for each worker node. [5a1e6df]
PDF page 47
Example 54Parallel Bitmap Heap Scan Detailed Information
postgres=> EXPLAIN (COSTS OFF, ANALYZE, VERBOSE) SELECT AVG(col1) FROM data1
WHERE col1 BETWEEN 1000000 AND 2000000 ;
QUERY PLAN
------------------------------------------------------------------------------
Finalize Aggregate (actual time=98.946..99.991 rows=1 loops=1)
Output: avg(col1)
Buffers: shared hit=8125 read=16
-> Gather (actual time=98.801..99.981 rows=3 loops=1)
…
-> Parallel Bitmap Heap Scan on public.data1 (actual time=46…
Output: col1, col2
Recheck Cond: ((data1.col1 >= 1000000) AND (data1.col1 …
Heap Blocks: exact=1598
Buffers: shared hit=8125 read=16
…
Record Count Output Format
Record counts (actual rows) are now output to two decimal places. [ddb17e3, 95dbd827]
Example 55Loop processing line count output
postgres=> EXPLAIN (ANALYZE, VERBOSE) SELECT * FROM data1 WHERE col1 < 1000 ;
QUERY PLAN
------------------------------------------------------------------------------
Index Scan using data1_pkey on public.data1 (cost=0.42..39.77 rows=1048
width=10) (actual time=0.008..0.084 rows=999.00 loops=1)
Output: col1, col2
Index Cond: (data1.col1 < 1000)
Buffers: shared hit=11
Planning:
Buffers: shared hit=4
Planning Time: 0.096 ms
Execution Time: 0.115 ms
(8 rows)
PDF page 48
Index Searches
The “Index Searches” item indicating the number of index searches is now output. [0fbceae]
Example 56Index Search Count
postgres=> EXPLAIN (ANALYZE, COSTS OFF) SELECT COUNT(*) FROM data1 WHERE
col1 IN (100, 1000, 10000) ;
QUERY PLAN
------------------------------------------------------------------------------
Aggregate (actual time=0.021..0.021 rows=1.00 loops=1)
Buffers: shared hit=10
-> Index Only Scan using data1_pkey on data1 (actual time=0.015..0.019 …
Index Cond: (col1 = ANY ('{100,1000,10000}'::integer[]))
Heap Fetches: 0
Index Searches: 3
…
Window Function Output
The “Window:” clause is now output in the execution plan when using Window functions. [8b1b342]
Example 57Window function output
postgres=> EXPLAIN SELECT AVG(col1) OVER (PARTITION BY col2) FROM data1 ;
QUERY PLAN
---------------------------------------------------------------------------
WindowAgg (cost=130488.33..146238.34 rows=1000000 width=36)
Window: w1 AS (PARTITION BY col2)
-> Sort (cost=128738.34..131238.34 rows=1000000 width=8)
Sort Key: col2
-> Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=8)
(5 rows)
PDF page 49
3.2.10 INSERT / UPDATE / DELETE / MERGE
The RETURNING clause of the update DML statement can now specify an OLD clause for preupdate information and a NEW clause for post-update information. The pre-update data (OLD) in the INSERT statement and the post-update data (NEW) in the DELETE statement will be NULL. [80feb72]
Example 58Output of old and new data in RETURNING clause
postgres=> UPDATE data1 SET col2='after' WHERE col1=100
RETURNING WITH (OLD AS o, NEW AS n) o.col1, o.col2, n.col2 ;
col1 | col2 | col2
-------+----------+---------
100 | before | after
(1 row)
postgres=> \pset null null
Null display is "null".
postgres=> DELETE FROM data1 WHERE col1=200 RETURNING OLD.*, NEW.* ;
col1 | col2 | col1 | col2
-------+----------+--------+--------
200 | before | null | null
(1 row)
3.2.11 VACUUM
The following new features have been added to the VACUUM statement.
ONLY clause
ONLY clause can now be specified for partitioned tables. No VACUUM processing is performed. [62ddf7e]
PDF page 50
Example 59Execute the VACUUM ONLY statement
postgres=> VACUUM VERBOSE ONLY part1 ;
WARNING: VACUUM ONLY of partitioned table "part1" has no effect
VACUUM
VERBOSE / Log output
When ‘on’ is specified for the parameter track_cost_delay_timing, ‘delay time’ is now output in the ANALYZE VERBOSE statement. The same information is also output to the log of automatic VACUUM. [7720082]
Example 60Execute the VACUUM VERBOSE statement
postgres=> VACUUM VERBOSE data1 ;
INFO: vacuuming "postgres.public.data1"
INFO: finished vacuuming "postgres.public.data1": index scans: 0
pages: 0 removed, 5406 remain, 1 scanned (0.02% of total), 0 eagerly scanned
tuples: 0 removed, 500000 remain, 0 are dead but not yet removable
removable cutoff: 786, which was 0 XIDs old when operation ended
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-
visible)
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item
identifiers removed
delay time: 0.000 ms
I/O timings: read: 0.000 ms, write: 0.000 ms
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
buffer usage: 13 hits, 0 reads, 0 dirtied
WAL usage: 0 records, 0 full page images, 0 bytes
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
VACUUM
Output WAL buffer full
The WAL buffer full count is now output when the VACUUM VERBOSE statement is executed. [6a8a7ce]
PDF page 51
Example 61Execute the VACUUM VERBOSE statement
postgres=> VACUUM VERBOSE data1 ;
INFO: vacuuming "postgres.public.data1"
INFO: finished vacuuming "postgres.public.data1": index scans: 0
pages: 0 removed, 5406 remain, 1 scanned (0.02% of total), 0 eagerly scanned
tuples: 0 removed, 500000 remain, 0 are dead but not yet removable
removable cutoff: 763, which was 0 XIDs old when operation ended
frozen: 0 pages from table (0.00% of total) had 0 tuples frozen
visibility map: 0 pages set all-visible, 0 pages set all-frozen (0 were all-
visible)
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item
identifiers removed
avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s
buffer usage: 13 hits, 0 reads, 0 dirtied
WAL usage: 0 records, 0 full page images, 0 bytes, 0 buffers full
system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s
VACUUM
Release of end-of-table free block
The parameter vacuum_truncate has been added. This parameter determines the behavior when the
The casefold function is almost identical to the lower function, but it performs lower case conversion according to collation. Its main purpose is to facilitate case-insensitive string comparisons. It can only be used when the database encoding is UTF-8. Some characters may change the character length. If the locale PG_UNICODE_FAST is used, ß (U+00DF) will be converted to ‘ss’. [bfc5992]
postgres=> CREATE TABLE data1(col1 INT PRIMARY KEY, col2 VARCHAR(10)) ;
CREATE TABLE
postgres=> GRANT SELECT, MAINTAIN ON data1 TO user1 ;
GRANT
postgres=> SELECT UNNEST(
pg_get_acl('pg_class'::regclass, 'data1'::regclass::oid, 0)) ;
unnest
--------------------
demo=arwdDxtm/demo
user1=rm/demo
(2 rows)
Pg_get_sequence_data function
The function pg_get_sequence_data, which returns a tuple of sequences, has been added. This function returns a NULL value if a sequence with no SELECT privilege is specified. This function is intended primarily for use in optimizing pg_dump commands. [c8b06bb, bd15b7d, a83a944]
PDF page 58
Syntax 15 - Pg_get_sequence_data function
recordpg_get_sequence_data(sequence_oidregclass)
Example 75Execution of the pg_get_sequence_data function
The function pg_ls_summariesdir has been added to PostgreSQL 18. This function provides a list of WAL summary files. The function can be executed by a user with the SUPERUSER attribute or a user authorized by the pg_maintain role. If the WAL summarize feature is disabled, no records are returned. [4e1fad3]
Syntax 16 - Pg_ls_summariesdir function
SETOFrecordpg_ls_summariesdir()
Column name
Data type
Description
name
text
WAL Summary File Name
size
bigint
Bytes in file
modification
timestamp with time zone
Last Modified
Table 18Column name and description of the return value of the pg_ls_summariesdir function
PDF page 59
Example 76Execution of the pg_ls_summariesdir function
The function pg_stat_get_backend_wal has been added to retrieve WAL output information for a specified backend process. The result of executing this function is similar to the pg_stat_wal view, but on a per-backend-process basis. [76def4c]
Syntax 17 - Pg_stats_get_backend_wal function
recordpg_stat_get_backend_wal(backend_pidinteger)
Column name
Data type
Description
wal_records
bigint
Number of WAL records
wal_fpi
bigint
Number of WAL full page images
wal_bytes
numeric
Number of WAL bytes
wal_buffers_full
bigint
Number of storage outputs due to WAL buffer full
stats_reset
timestamp with time zone
Data reset time
Table 19Column name and description of the return value of the pg_stat_get_backend_wal function
Example 77Execution of the pg_stat_get_backend_wal function
Argument names have been added to the regular expression function (regexp*). [580f872]
Example 78Argument name of the regexp_count function
postgres=> \df regexp_count
List of functions
Schema | Name | Result data type | Argument data
------------+--------------+------------------+-------------------------------
pg_catalog | regexp_count | integer | string text, pattern text …
pg_catalog | regexp_count | integer | string text, pattern text, …
pg_catalog | regexp_count | integer | string text, pattern text, …
(3 rows)
To_number function and to_char function
The to_number function and to_char function now accept an RN indicating a Roman numeral as the format specification character. The case of the input string and the format specification character is ignored. [172e6b3]
Example 79Specify Roman numerals in the to_number function
Uuidv7 function supporting UUID v7 and uuidv4 function supporting UUID v4 are added. uuidv7 is expected to suppress randomization of index storage pages by specifying the timestamp of function execution in the first part of uuidv7. The uuidv7 function is expected to suppress randomization of index storage pages by specifying a timestamp at the beginning of the function. [78c5e14]
Syntax 18 - UUIDv4 / UUIDv7 function
uuiduuidv7([shiftinterval])uuiduuidv4()
PDF page 61
Example 80Execution of the uuidv7 function and the uuidv4 function
OR conditions for indexed columns are now automatically converted to ANY clauses. [d4378c0, ae45691]
Example 82Execution Plan of PostgreSQL 17
postgres=> EXPLAIN SELECT * FROM data1 WHERE col1=1000 OR col1=2000 ;
QUERY PLAN
------------------------------------------------------------------------------
Bitmap Heap Scan on data1 (cost=8.87..16.78 rows=2 width=10)
Recheck Cond: ((col1 = 1000) OR (col1 = 2000))
-> BitmapOr (cost=8.87..8.87 rows=2 width=0)
-> Bitmap Index Scan on data1_pkey (cost=0.00..4.43 rows=1 width=0)
Index Cond: (col1 = 1000)
-> Bitmap Index Scan on data1_pkey (cost=0.00..4.43 rows=1 width=0)
Index Cond: (col1 = 2000)
(7 rows)
Example 83Execution Plan of PostgreSQL 18
postgres=> EXPLAIN SELECT * FROM data1 WHERE col1=1000 OR col1=2000 ;
QUERY PLAN
--------------------------------------------------------------------------
Index Scan using data1_pkey on data1 (cost=0.42..12.88 rows=2 width=10)
Index Cond: (col1 = ANY ('{1000,2000}'::integer[]))
(2 rows)
ANY Conversion of Join OR Conditions
When an OR is used in a join condition, it can now be converted to an ANY clause. [627d634]
PDF page 63
Example 84Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM data1 AS d1, data2 AS d2
WHERE d1.col1 = d2.col1 OR d1.col1 = d2.col2 ;
QUERY PLAN
------------------------------------------------------------------------
Finalize Aggregate
-> Gather
Workers Planned: 2
-> Partial Aggregate
-> Nested Loop
-> Parallel Seq Scan on data1 d1
-> Bitmap Heap Scan on data2 d2
Recheck Cond: ((d1.col1 = col1) OR (d1.col1 = col2))
-> BitmapOr
-> Bitmap Index Scan on data2_pkey
Index Cond: (col1 = d1.col1)
-> Bitmap Index Scan on idx1_data2
Index Cond: (co;2 = d1.col1)
(13 rows)
Example 85Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM data1 AS d1, data2 AS d2
WHERE d1.col1 = d2.col1 OR d1.col2 = d2.col2 ;
QUERY PLAN
------------------------------------------------------------------------
Finalize Aggregate
-> Gather
Workers Planned: 2
-> Partial Aggregate
-> Nested Loop
-> Parallel Seq Scan on data2 d2
-> Index Only Scan using data1_pkey on data1 d1
Index Cond: (col1 = ANY (ARRAY[d2.col1, d2.col2]))
(8 rows)
PDF page 64
IN (VALUES) Clause Converted to ANY Clause
The WHERE clause with IN (VALUES) is converted to an ANY clause. [c0962a1]
Example 86Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT * FROM data1
WHERE col1 IN (VALUES(100), (200)) ;
QUERY PLAN
-----------------------------------------------
Nested Loop
-> Unique
-> Sort
Sort Key: "*VALUES*".column1
-> Values Scan on "*VALUES*"
-> Index Scan using data1_pkey on data1
Index Cond: (col1 = "*VALUES*".column1)
(7 rows)
Example 87Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT * FROM data1
WHERE col1 IN (VALUES(100), (200)) ;
QUERY PLAN
---------------------------------------------------
Index Scan using data1_pkey on data1
Index Cond: (col1 = ANY ('{100,200}'::integer[]))
(2 rows)
Right Semi Join
Right Semi Join is now available as the execution plan. It may be adopted for hash joins or merge joins. [aa86129e]
PDF page 65
Example 88Adoption of Right Semi Join
postgres=> EXPLAIN (COSTS OFF, VERBOSE)
SELECT COUNT(*) FROM tenk1 a WHERE (unique1, two) IN
(SELECT unique1, ROW_NUMBER() OVER() FROM tenk1 b) ;
QUERY PLAN
------------------------------------------------------------------------------
Aggregate
Output: count(*)
-> Hash Right Semi Join
Hash Cond: ((b.unique1 = a.unique1) AND ((row_number() OVER (?))…
-> WindowAgg
Output: b.unique1, row_number() OVER (?)
-> Seq Scan on public.tenk1 b
Output: b.unique1
-> Hash
Output: a.unique1, a.two
-> Seq Scan on public.tenk1 a
Output: a.unique1, a.two
(12 rows)
UNIQUE Constraints and GROUP BY
When a column specified by a NOT NULL constraint and a UNIQUE index is included in a GROUP BY clause, non-target columns are now excluded from GROUP BY. The following example shows a col1 column with UNIQUE and NOT NULL constraints specified. [bd10ec5]
Example 89Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT col1, col2 FROM data1
GROUP BY col1, col2 ;
QUERY PLAN
-------------------------
HashAggregate
Group Key: col1, col2
-> Seq Scan on data1
(3 rows)
PDF page 66
Example 90Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT col1, col2 FROM data1
GROUP BY col1, col2 ;
QUERY PLAN
-------------------------
HashAggregate
Group Key: col1
-> Seq Scan on data1
(3 rows)
Optimization of the DISTINCT clause
Since the order of columns specified in the DISTINCT clause is not meaningful, optimization has been implemented to reorder them as needed. A new parameter, enable_distinct_reordering, has been added to change this behavior. The default value of this parameter is ‘on’, which causes the optimization to occur; to revert to PostgreSQL 17 behavior, set this parameter to ‘off’. In the example below, column col1 is modified in the execution plan because it is a primary key, and the processing of column col2 is disabled. [a8ccf4e]
Example 91Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT DISTINCT col2, col1 FROM data1 ;
QUERY PLAN
-------------------------
HashAggregate
Group Key: col2, col1
-> Seq Scan on data1
(3 rows)
PDF page 67
Example 92Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT DISTINCT col2, col1 FROM data1 ;
QUERY PLAN
--------------------------------------------------
Unique
-> Incremental Sort
Sort Key: col1, col2
Presorted Key: col1
-> Index Scan using data1_pkey on data1
(5 rows)
Self-join optimization
The optimization of replacing self-join operations with unjoin scans when the execution result is unaffected has been implemented. This behavior is controlled by the parameter enable_self_join_elimination. The default value is ‘on’ and the optimization is executed. In the following example, column col1 of table data1 is the primary key. [fc069a3]
Example 93Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT * FROM data1 d1, data1 d2 WHERE
d1.col1 = d2.col1 ;
QUERY PLAN
----------------------------------
Hash Join
Hash Cond: (d1.col1 = d2.col1)
-> Seq Scan on data1 d1
-> Hash
-> Seq Scan on data1 d2
(5 rows)
PDF page 68
Example 94Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT * FROM data1 d1, data1 d2 WHERE
d1.col1 = d2.col1 ;
QUERY PLAN
----------------------
Seq Scan on data1 d2
(1 row)
Index Skip Scan
For an index consisting of multiple columns, if a match condition other than the first column is specified, the execution plan that uses the corresponding index is now created. In the following example, a composite index is created for columns col3 and col2, in that order.
Example 95Execution Plan of PostgreSQL 17
postgres=> EXPLAIN SELECT * FROM data2 WHERE col2=100 ;
QUERY PLAN
----------------------------------------------------------
Seq Scan on data2 (cost=0.00..17906.00 rows=1 width=14)
Filter: (col2 = 100)
(2 rows)
Example 96Execution Plan of PostgreSQL 18
postgres=> EXPLAIN SELECT * FROM data2 WHERE col2=100 ;
QUERY PLAN
--------------------------------------------------------------------------
Index Scan using idx2_data2 on data2 (cost=0.42..12.88 rows=1 width=14)
Index Cond: (col2 = 100)
(2 rows)
Transformation of HAVING clauses into WHERE clauses
The optimization of transforming some HAVING clauses used in GROUPING SETS clauses into WHERE clauses has been implemented. [67a54b9]
PDF page 69
Example 97Execution Plan of PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT col1, col2, COUNT(col1) FROM data1
GROUP BY GROUPING SETS ((col1, col2), (col1)) HAVING col1 > 1000 AND col2 >
1000 ;
QUERY PLAN
---------------------------------------------
GroupAggregate
Group Key: col1, col2
Group Key: col1
Filter: ((col1 > 1000) AND (col2 > 1000))
-> Sort
Sort Key: col1, col2
-> Seq Scan on data1
(7 rows)
Example 98Execution Plan of PostgreSQL 18
postgres=> EXPLAIN (COSTS OFF) SELECT col1, col2, COUNT(col1) FROM data1
GROUP BY GROUPING SETS ((col1, col2), (col1)) HAVING col1 > 1000 AND col2 >
1000 ;
QUERY PLAN
-------------------------------------
GroupAggregate
Group Key: col1, col2
Group Key: col1
Filter: (col2 > 1000)
-> Sort
Sort Key: col1, col2
-> Seq Scan on data1
Filter: (col1 > 1000)
(8 rows)