{"id":365404,"date":"2024-05-21T02:37:25","date_gmt":"2024-05-21T02:37:25","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=365404"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-05-21T02:37:25","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=365404","title":{"rendered":"<span>PostgreSQL 17: Part 3 or Commitfest 2023-11<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ua\/k3\/zc\/uak3zcegyyo1tirna66hij1f_44.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ua\/k3\/zc\/uak3zcegyyo1tirna66hij1f_44.png\"\/><\/p>\n<p>  <\/p>\n<p>The November commitfest is ripe with new interesting features! Without further ado, let&#8217;s proceed with the review.<\/p>\n<p>  <\/p>\n<p>If you missed our July and September commitfest reviews, you can check them out here: <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-07<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-09<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"#commit_e83d1b0c\">ON LOGIN trigger<\/a><br \/>  <a href=\"#commit_f21848de\">Event triggers for REINDEX<\/a><br \/>  <a href=\"#commit_2b5154be\">ALTER OPERATOR: commutator, negator, hashes, merges<\/a><br \/>  <a href=\"#commit_a5cf808b\">pg_dump &#8212;filter=dump.txt<\/a><br \/>  <a href=\"#commit_d1379ebf\">psql: displaying default privileges<\/a><br \/>  <a href=\"#commit_dc9f8a79\">pg_stat_statements: track statement entry timestamps and reset min\/max statistics<\/a><br \/>  <a href=\"#commit_96f05261\">pg_stat_checkpointer: checkpointer process statistics<\/a><br \/>  <a href=\"#commit_bc3c8db8\">pg_stats: statistics for range type columns<\/a><br \/>  <a href=\"#commit_d3d55ce5\">Planner: exclusion of unnecessary table self-joins<\/a><br \/>  <a href=\"#commit_f7816aec\">Planner: materialized CTE statistics<\/a><br \/>  <a href=\"#commit_5d8aa8bc\">Planner: accessing a table with multiple clauses<\/a><br \/>  <a href=\"#commit_e0b1ee17\">Index range scan optimization<\/a><br \/>  <a href=\"#commit_c789f0f6\">dblink, postgres_fdw: detailed wait events<\/a><br \/>  <a href=\"#commit_29d0a77f\">Logical replication: migration of replication slots during publisher upgrade<\/a><br \/>  <a href=\"#commit_7c3fb505\">Replication slot use log<\/a><br \/>  <a href=\"#commit_a02b37fc\">Unicode: new information functions<\/a><br \/>  <a href=\"#commit_526fe0d7\">New function: xmltext<\/a><br \/>  <a href=\"#commit_97957fdb\">AT LOCAL support<\/a><br \/>  <a href=\"#commit_519fc1bd\">Infinite intervals<\/a><br \/>  <a href=\"#commit_2d870b4a\">ALTER SYSTEM with unrecognized custom parameters<\/a><br \/>  <a href=\"#commit_721856ff\">Building the server from source<\/a><\/p>\n<p><a name=\"habracut\"><\/a><br \/>  <a name=\"commit_e83d1b0c\"><\/a>  <\/p>\n<h2 id=\"on-login-triggerhttpscommitfestpostgresqlorg452900\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/2900\/\"><strong>ON LOGIN trigger<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/e83d1b0c\">e83d1b0c<\/a><\/p>\n<p>  <\/p>\n<p>The ability to put event an trigger on a database login event is coming to PostgreSQL.<\/p>\n<p>  <\/p>\n<p>As usual, the trigger is created in two steps. First, the trigger function:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE FUNCTION check_login() RETURNS event_trigger AS $$ BEGIN     IF session_user = 'postgres' THEN RETURN; END IF;      IF to_char(current_date, 'DY') IN ('SAT','SUN')     THEN         RAISE 'Have a nice weekend, see you on Monday!';     END IF; END; $$ LANGUAGE plpgsql;<\/code><\/pre>\n<p>  <\/p>\n<p>Then the trigger itself:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE EVENT TRIGGER check_login     ON LOGIN     EXECUTE FUNCTION check_login();<\/code><\/pre>\n<p>  <\/p>\n<p>Now, to everyone&#8217;s delight, users will not bother the administrator on weekends. ?<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ psql -U alice -d postgres<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">psql: error: connection to server on socket \"\/tmp\/.s.PGSQL.5401\" failed: FATAL:  Have a nice weekend, see you on Monday! CONTEXT:  PL\/pgSQL function check_login() line 6 at RAISE<\/code><\/pre>\n<p>  <\/p>\n<p>See also<br \/>  <a href=\"https:\/\/www.depesz.com\/2023\/10\/24\/waiting-for-postgresql-17-add-support-event-triggers-on-authenticated-login\/\">Waiting for PostgreSQL 17 \u2013 Add support event triggers on authenticated login \u2013 select * from depesz;<\/a><\/p>\n<p>  <a name=\"commit_f21848de\"><\/a>  <\/p>\n<h2 id=\"event-triggers-for-reindexhttpscommitfestpostgresqlorg454462\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4462\/\"><strong>Event triggers for REINDEX<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/f21848de\">f21848de<\/a><\/p>\n<p>  <\/p>\n<p>Event triggers now work with the REINDEX command, allowing you to create triggers for <code>ddl_command_start<\/code> and <code>ddl_command_end<\/code> events.<\/p>\n<p>  <a name=\"commit_2b5154be\"><\/a>  <\/p>\n<h2 id=\"alter-operator-commutator-negator-hashes-mergeshttpscommitfestpostgresqlorg454389\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4389\/\"><strong>ALTER OPERATOR: commutator, negator, hashes, merges<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/2b5154be\">2b5154be<\/a><\/p>\n<p>  <\/p>\n<p>In the <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/sql-alteroperator.html\">ALTER OPERATOR<\/a> command, you can now specify a commutator and a negator for the operator being defined, if they were not specified in the CREATE OPERATOR already. You can also add support for hash join and merge join operations (HASHES and MERGES)<\/p>\n<p>  <a name=\"commit_a5cf808b\"><\/a>  <\/p>\n<h2 id=\"pg_dump---filterdumptxthttpscommitfestpostgresqlorg452573\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/2573\/\"><strong>pg_dump &#8212;filter=dump.txt<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/a5cf808b\">a5cf808b<\/a><\/p>\n<p>  <\/p>\n<p>The new parameter <code>--filter<\/code> allows the user to specify a file name with a list of objects to include or exclude from the dump:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ cat dump.txt<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">include table      bookings exclude table_data bookings<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_dump -d demo --filter=dump.txt |grep -v -E '^SET|^SELECT|^--|^$'<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">CREATE TABLE bookings.bookings (     book_ref character(6) NOT NULL,     book_date timestamp with time zone NOT NULL,     total_amount numeric(10,2) NOT NULL ); ALTER TABLE bookings.bookings OWNER TO postgres; COMMENT ON TABLE bookings.bookings IS 'Bookings'; COMMENT ON COLUMN bookings.bookings.book_ref IS 'Booking number'; COMMENT ON COLUMN bookings.bookings.book_date IS 'Booking date'; COMMENT ON COLUMN bookings.bookings.total_amount IS 'Total booking cost'; ALTER TABLE ONLY bookings.bookings     ADD CONSTRAINT bookings_pkey PRIMARY KEY (book_ref);<\/code><\/pre>\n<p>  <\/p>\n<p>The exact file syntax is described in the <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/app-pgdump.html\">documentation<\/a>.<\/p>\n<p>  <\/p>\n<p>This parameter may come in handy when the list of database objects is so long that it does not fit into the <code>pg_dump<\/code> command&#8217;s character limit.<\/p>\n<p>  <\/p>\n<p><code>--filter<\/code> has also been added to <code>pg_dumpall<\/code> and <code>pg_restore<\/code>.<\/p>\n<p>  <a name=\"commit_d1379ebf\"><\/a>  <\/p>\n<h2 id=\"psql-displaying-default-privilegeshttpscommitfestpostgresqlorg454593\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4593\/\"><strong>psql: displaying default privileges<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/d1379ebf\">d1379ebf<\/a><\/p>\n<p>  <\/p>\n<p>psql commands were never a good way to view default privileges. We will use schemas in this example, although the same logic applies to any type of database object.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE SCHEMA s;<\/code><\/pre>\n<p>  <\/p>\n<p>When a schema is created, its owner gets both its privileges, USAGE and CREATE, by default. However, if you try to check the privileges using the <code>\\dn+<\/code> command, it will return NULL, since the privileges aren&#8217;t recorded into <code>pg_namespace.nspacl<\/code>.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# SELECT nspacl IS NULL FROM pg_namespace WHERE oid = 's'::regnamespace;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> ?column? ----------  t<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# \\pset null '(null)' 16=# \\dn+ s<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                  List of schemas  Name |  Owner   | Access privileges | Description ------+----------+-------------------+-------------  s    | postgres |                   |<\/code><\/pre>\n<p>  <\/p>\n<p>Note how even if you set <code>\\pset null<\/code>, it does not affect the NULL values being displayed. When viewing system catalog objects, psql commands used to ignore this setting.<\/p>\n<p>  <\/p>\n<p>Running any GRANT or REVOKE command will have the privileges recorded into the system catalog explicitly. The following commands grant and revoke privileges for the schema. Nothing changes as a result, except that the default privileges now appear in the table:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# GRANT USAGE ON SCHEMA s TO public; 16=# REVOKE USAGE ON SCHEMA s FROM public;  16=# \\dn+ s<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                   List of schemas  Name |  Owner   |  Access privileges   | Description ------+----------+----------------------+-------------  s    | postgres | postgres=UC\/postgres |<\/code><\/pre>\n<p>  <\/p>\n<p>The owner may revoke their own privileges (and grant them back again later):<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# REVOKE ALL ON SCHEMA s FROM postgres;<\/code><\/pre>\n<p>  <\/p>\n<p>At the moment, <code>pg_namespace.nspacl<\/code> is an empty <code>aclitem[]<\/code> array. An empty array does not equal NULL, but how to distinguish between the two in the command output?<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# \\dn+ s<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                  List of schemas  Name |  Owner   | Access privileges | Description ------+----------+-------------------+-------------  s    | postgres |                   |<\/code><\/pre>\n<p>  <\/p>\n<p>The only solution used to be to query <em>pg_namespace<\/em>.<\/p>\n<p>  <\/p>\n<p>In PostgreSQL 17 this is no longer the case. The <code>\\pset null<\/code> setting now properly affects NULL values:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# CREATE SCHEMA s;  17=# \\pset null '(null)' 17=# \\dn+ s<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                  List of schemas  Name |  Owner   | Access privileges | Description ------+----------+-------------------+-------------  s    | postgres | (null)            | (null)<\/code><\/pre>\n<p>  <\/p>\n<p>And the absence of privileges (an empty array) is displayed with a special value <code>(none)<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# REVOKE ALL ON SCHEMA s FROM postgres; 17=# \\dn+ s<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                  List of schemas  Name |  Owner   | Access privileges | Description ------+----------+-------------------+-------------  s    | postgres | (none)            | (null)<\/code><\/pre>\n<p>  <a name=\"commit_dc9f8a79\"><\/a>  <\/p>\n<h2 id=\"pg_stat_statements-track-statement-entry-timestamps-and-reset-minmax-statistics-httpscommitfestpostgresqlorg453048\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/3048\/\"><strong>pg_stat_statements: track statement entry timestamps and reset min\/max statistics<\/strong> <\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/dc9f8a79\">dc9f8a79<\/a><\/p>\n<p>  <\/p>\n<p><code>pg_stat_statements<\/code> now has a new column <code>stats_since<\/code>, which records the time when collecting statistics for each operator was initiated. In addition to that, you can now reset the statistics in the <code>min\/max*<\/code> columns for specific operators by calling <code>pg_stat_statements<\/code> with the parameter <code>minmax_only<\/code>. The reset time for the statistics is recorded in the <code>minmax_stats_since<\/code> column.<\/p>\n<p>  <\/p>\n<p>These new features will come in handy for monitoring systems that rely on sampling information from <code>pg_stat_statements<\/code>. In particular, they may skip collecting all the statistics before starting a snapshot.<\/p>\n<p>  <a name=\"commit_96f05261\"><\/a>  <\/p>\n<h2 id=\"pg_stat_checkpointer-checkpointer-process-statisticshttpscommitfestpostgresqlorg454014\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4014\/\"><strong>pg_stat_checkpointer: checkpointer process statistics<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/96f05261\">96f05261<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/74604a37\">74604a37<\/a><\/p>\n<p>  <\/p>\n<p>Buffer cache changes can be written to disk by three types of processes: background writer, checkpointer, and user backend processes. For the longest time, all the relevant statistics have been tracked in a single <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/monitoring-stats.html#MONITORING-PG-STAT-BGWRITER-VIEW\"><code>pg_stat_bgwriter<\/code> view<\/a>.<\/p>\n<p>  <\/p>\n<p>The new patch trims the number of columns in <code>pg_stat_bgwriter<\/code> down considerably. All checkpointer statistics have been moved into a new view <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/monitoring-stats.html#MONITORING-PG-STAT-CHECKPOINTER-VIEW\"><code>pg_stat_checkpointer<\/code><\/a> (first commit).<\/p>\n<p>  <\/p>\n<p>At the same time, the <code>buffers_backend<\/code> and <code>buffers_backend_fsync<\/code> columns were removed from <code>pg_stat_bgwriter<\/code>, since more accurate and detailed information about the backend processes can now be found in the <code>pg_stat_io<\/code> view that was introduced in <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/monitoring-stats.html#MONITORING-PG-STAT-IO-VIEW\">PostgreSQL 16<\/a> (second commit).<\/p>\n<p>  <a name=\"commit_bc3c8db8\"><\/a>  <\/p>\n<h2 id=\"pg_stats-statistics-for-range-type-columnshttpscommitfestpostgresqlorg453184\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/3184\/\"><strong>pg_stats: statistics for range type columns<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/bc3c8db8\">bc3c8db8<\/a><\/p>\n<p>  <\/p>\n<p>Statistics for range type columns have traditionally been collected and stored in the <code>pg_statistic<\/code> table. However, this information was not displayed in the <code>pg_stats<\/code> view.<\/p>\n<p>  <\/p>\n<p>This patch fixes that by adding three new columns to <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/view-pg-stats.html\"><code>pg_stats<\/code><\/a>: <code>range_length_histogram<\/code>, <code>range_empty_frac<\/code>, and <code>range_bounds_histogram<\/code>.<\/p>\n<p>  <a name=\"commit_d3d55ce5\"><\/a>  <\/p>\n<h2 id=\"planner-exclusion-of-unnecessary-table-self-joinshttpscommitfestpostgresqlorg451712\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/1712\/\"><strong>Planner: exclusion of unnecessary table self-joins<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/d3d55ce5\">d3d55ce5<\/a><\/p>\n<p>  <\/p>\n<p>In a poorly composed query, a table may be joined with itself for any reason, as the SQL syntax allows it. Such queries often come from all sorts of ORMs, and the burden of optimisation often falls onto those who are powerless to change the way the queries are structured in the first place.<\/p>\n<p>  <\/p>\n<p>In PostgreSQL 17, the planner now can catch these unnecessary joins and exclude them from the query plan. The following example demonstrates an unnecessary semijoin of the table bookings with itself.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# EXPLAIN (costs off) WITH b AS (     SELECT book_ref FROM bookings ) SELECT * FROM bookings WHERE book_ref IN (SELECT book_ref FROM b);<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">            QUERY PLAN             ----------------------------------  Seq Scan on bookings    Filter: (book_ref IS NOT NULL)<\/code><\/pre>\n<p>  <\/p>\n<p>The planner is justified in deciding that the table should only be scanned once.<\/p>\n<p>  <\/p>\n<p>This optimization is toggled by the parameter <em>enable_self_join_removal<\/em>. Switching it off leads to the behavior observed in the older versions:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# SET enable_self_join_removal = off;  17=# EXPLAIN (costs off) WITH b AS (     SELECT book_ref FROM bookings ) SELECT * FROM bookings WHERE book_ref IN (SELECT book_ref FROM b);<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                       QUERY PLAN                        --------------------------------------------------------  Hash Join    Hash Cond: (bookings.book_ref = bookings_1.book_ref)    ->  Seq Scan on bookings    ->  Hash          ->  Seq Scan on bookings bookings_1<\/code><\/pre>\n<p>  <a name=\"commit_f7816aec\"><\/a>  <\/p>\n<h2 id=\"planner-materialized-cte-statisticshttpscommitfestpostgresqlorg454510\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4510\/\">Planner: materialized CTE statistics<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/f7816aec\">f7816aec<\/a><\/p>\n<p>  <\/p>\n<p>Let&#8217;s try the same example, but with CTE materialization. This is how it plays out in PostgreSQL 16 (jit off):<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (analyze,timing off) WITH b AS MATERIALIZED (     SELECT book_ref FROM bookings ) SELECT * FROM bookings WHERE book_ref IN (SELECT book_ref FROM b);<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                                    QUERY PLAN                                                      -------------------------------------------------------------------------------------------------------------------  Nested Loop  (cost=82058.51..83469.66 rows=1055555 width=21) (actual rows=2111110 loops=1)    CTE b      ->  Seq Scan on bookings bookings_1  (cost=0.00..34558.10 rows=2111110 width=7) (actual rows=2111110 loops=1)    ->  HashAggregate  (cost=47499.98..47501.98 rows=200 width=28) (actual rows=2111110 loops=1)          Group Key: b.book_ref          Batches: 141  Memory Usage: 11113kB  Disk Usage: 57936kB          ->  CTE Scan on b  (cost=0.00..42222.20 rows=2111110 width=28) (actual rows=2111110 loops=1)    ->  Index Scan using bookings_pkey on bookings  (cost=0.43..8.37 rows=1 width=21) (actual rows=1 loops=2111110)          Index Cond: (book_ref = b.book_ref)  Planning Time: 0.237 ms  Execution Time: 10847.689 ms                                                    <\/code><\/pre>\n<p>  <\/p>\n<p>The planner has decided to group the CTE by <code>book_ref<\/code> before joining with the outer query (the HashAggregate node). But <code>book_ref<\/code> is the primary key of the bookings table, so the number of rows after grouping will remain at ~2 million, not at the estimated 200 rows. As a result, the planner selects a wrong join type (nested loop) for the CTE and the outer query.<\/p>\n<p>  <\/p>\n<p>In PostgreSQL 17, the planner has access to some statistics about columns from materialized CTE and can use those in the outer parts of the plan. This leads to more precise cardinality estimates. This is how the same query runs now:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                                    QUERY PLAN                                                      -------------------------------------------------------------------------------------------------------------------  Hash Semi Join  (cost=117601.18..222997.93 rows=2111110 width=21) (actual rows=2111110 loops=1)    Hash Cond: (bookings.book_ref = b.book_ref)    CTE b      ->  Seq Scan on bookings bookings_1  (cost=0.00..34558.10 rows=2111110 width=7) (actual rows=2111110 loops=1)    ->  Seq Scan on bookings  (cost=0.00..34558.10 rows=2111110 width=21) (actual rows=2111110 loops=1)    ->  Hash  (cost=42222.20..42222.20 rows=2111110 width=28) (actual rows=2111110 loops=1)          Buckets: 131072  Batches: 32  Memory Usage: 3529kB          ->  CTE Scan on b  (cost=0.00..42222.20 rows=2111110 width=28) (actual rows=2111110 loops=1)  Planning Time: 0.127 ms  Execution Time: 1556.576 ms<\/code><\/pre>\n<p>  <\/p>\n<p>With the unnecessary grouping gone and using hash join for the CTE and the outer query, the execution is about 7 times faster.<\/p>\n<p>  <a name=\"commit_5d8aa8bc\"><\/a>  <\/p>\n<h2 id=\"planner-accessing-a-table-with-multiple-clauseshttpscommitfestpostgresqlorg454605\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4605\/\">Planner: accessing a table with multiple clauses<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/5d8aa8bc\">5d8aa8bc<\/a><\/p>\n<p>  <\/p>\n<p>When a table is queried with multiple restriction clauses, should it always be queried in the first place? This patch introduces a small optimisation along this line of thinking.<\/p>\n<p>  <\/p>\n<p>Consider this query in PostgreSQL 16.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (costs off, analyze, timing off, summary off) SELECT * FROM tickets WHERE ticket_no = '0005432' AND ticket_no = '0005000';<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                           QUERY PLAN                             -----------------------------------------------------------------  Result (actual rows=0 loops=1)    One-Time Filter: false    ->  Index Scan using tickets_pkey on tickets (never executed)          Index Cond: (ticket_no = '0005432'::bpchar)<\/code><\/pre>\n<p>  <\/p>\n<p>Two clauses for the tickets table contradict each other, but the planner does not immediately figure this out and proceeds to select a table access method. It concludes at a later stage that there is no need to access the table after all, but the resources spent on planning end up wasted.<\/p>\n<p>  <\/p>\n<p>Now, here is the plan for the same query in PostgreSQL 17:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">           QUERY PLAN            --------------------------------  Result (actual rows=0 loops=1)    One-Time Filter: false<\/code><\/pre>\n<p>  <\/p>\n<p>Both the clauses are analyzed in advance. The plan not only becomes cheaper to build, but looks much neater as well.<\/p>\n<p>  <a name=\"commit_e0b1ee17\"><\/a>  <\/p>\n<h2 id=\"index-range-scan-optimizationhttpscommitfestpostgresqlorg454434\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4434\/\">Index range scan optimization<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/e0b1ee17\">e0b1ee17<\/a><\/p>\n<p>  <\/p>\n<p>When a range scan is performed using a B-tree index, each scanned index page is searched for values within the given range.<\/p>\n<p>  <\/p>\n<p>Previously, PostgreSQL used to check if every value in an index page is within the range. With the new patch, it first checks the last value in a page: if the last value is within the range, so must be all other values. The more index pages are there to scan and the more expensive the comparison operator, the greater the optimization effect.<\/p>\n<p>  <\/p>\n<p>In the following example, more than 40 thousand index pages are scanned. This is the query in PostgreSQL 16:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (analyze, buffers, costs off, timing off) SELECT * FROM tickets WHERE ticket_no > '0005432' AND ticket_no &lt; '0005434';<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                     QUERY PLAN                                       -------------------------------------------------------------------------------------  Index Scan using tickets_pkey on tickets (actual rows=1469571 loops=1)    Index Cond: ((ticket_no > '0005432'::bpchar) AND (ticket_no &lt; '0005434'::bpchar))    Buffers: shared hit=10675 read=30185  Planning:    Buffers: shared read=4  Planning Time: 0.214 ms  Execution Time: 683.801 ms<\/code><\/pre>\n<p>  <\/p>\n<p>The same query in PostgreSQL 17 executes much faster:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                     QUERY PLAN                                       -------------------------------------------------------------------------------------  Index Scan using tickets_pkey on tickets (actual rows=1469571 loops=1)    Index Cond: ((ticket_no > '0005432'::bpchar) AND (ticket_no &lt; '0005434'::bpchar))    Buffers: shared hit=10690 read=30170  Planning:    Buffers: shared read=4  Planning Time: 0.268 ms  Execution Time: 237.177 ms<\/code><\/pre>\n<p>  <a name=\"commit_c789f0f6\"><\/a>  <\/p>\n<h2 id=\"dblink-postgres_fdw-detailed-wait-eventshttpscommitfestpostgresqlorg454506\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4506\/\">dblink, postgres_fdw: detailed wait events<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/c789f0f6\">c789f0f6<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/d61f2538\">d61f2538<\/a><\/p>\n<p>  <\/p>\n<p>Extensions <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/dblink.html\">dblink<\/a>, <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/postgres-fdw.html#POSTGRES-FDW-WAIT-EVENTS\">postgres_fdw<\/a> first started using the <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285#commit_c9af0546\">new interface<\/a> for creating wait events. The new wait events are explained in each extension&#8217;s documentation .<\/p>\n<p>  <a name=\"commit_29d0a77f\"><\/a>  <\/p>\n<h2 id=\"logical-replication-migration-of-replication-slots-during-publisher-upgradehttpscommitfestpostgresqlorg454273\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4273\/\">Logical replication: migration of replication slots during publisher upgrade<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/29d0a77f\">29d0a77f<\/a><\/p>\n<p>  <\/p>\n<p>Upgrading the publisher with pg_upgrade creates an issue with logical replication. Publications migrate to the new version, but replication slots do not. This forces subscribers to synchronize data again when the publisher is upgraded.<\/p>\n<p>  <\/p>\n<p>This patch makes pg_upgrade migrate the replication slots to the new server, so the subscribers only have to adjust the connection string and then carry on collecting updates.<\/p>\n<p>  <a name=\"commit_7c3fb505\"><\/a>  <\/p>\n<h2 id=\"replication-slot-use-loghttpscommitfestpostgresqlorg453492\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/3492\/\">Replication slot use log<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/7c3fb505\">7c3fb505<\/a><\/p>\n<p>  <\/p>\n<p>In addition to logging replication commands, the parameter <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/runtime-config-logging.html#GUC-LOG-REPLICATION-COMMANDS\"><em>log_replication_commands<\/em><\/a> now also enables logging of wal sender processes acquiring and releasing replication slots.<\/p>\n<p>  <\/p>\n<p>Analyzing these log messages may show how long replication slots remain idle, which in turn may help hunt down issues with consumers (such as replicas or logical replication subscribers).<\/p>\n<p>  <\/p>\n<p>Let&#8217;s enable <em>log_replication_commands<\/em> and make a backup using <code>pg_basebackup<\/code>. There are new messages in the log:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">LOG:  acquired physical replication slot \"pg_basebackup_339820\" \u2026 LOG:  released physical replication slot \"pg_basebackup_339820\"<\/code><\/pre>\n<p>  <a name=\"commit_a02b37fc\"><\/a>  <\/p>\n<h2 id=\"unicode-new-information-functionshttpscommitfestpostgresqlorg454604\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4604\/\">Unicode: new information functions<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/a02b37fc\">a02b37fc<\/a><\/p>\n<p>  <\/p>\n<p>The function <code>unicode_assigned<\/code> returns True if all every character in a string has a Unicode code point assigned:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT unicode_assigned('Hello World!');<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> unicode_assigned ------------------  t<\/code><\/pre>\n<p>  <\/p>\n<p>The other two functions return the Unicode version for PostgreSQL and ICU:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT unicode_version(), icu_unicode_version();<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> unicode_version | icu_unicode_version -----------------+---------------------  15.1            | 14.0<\/code><\/pre>\n<p>  <a name=\"commit_526fe0d7\"><\/a>  <\/p>\n<h2 id=\"new-function-xmltexthttpscommitfestpostgresqlorg454257\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4257\/\"><strong>New function: xmltext<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/526fe0d7\">526fe0d7<\/a><\/p>\n<p>  <\/p>\n<p>xmltext is a standard SQL function. It converts an input string into an XML value, properly escaping any special characters:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT xmltext('&lt;Hello &amp; World>');<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">         xmltext           --------------------------  &amp;lt;\u041f\u0440\u0438\u0432\u0435\u0442 &amp;amp; \u041c\u0438\u0440&amp;gt;<\/code><\/pre>\n<p>  <a name=\"commit_97957fdb\"><\/a>  <\/p>\n<h2 id=\"at-local-supporthttpscommitfestpostgresqlorg454343\"><strong><a href=\"https:\/\/commitfest.postgresql.org\/45\/4343\/\">AT LOCAL support<\/a><\/strong><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/97957fdb\">97957fdb<\/a><\/p>\n<p>  <\/p>\n<p>The construction AT TIME ZONE is used to explicitly specify a time zone when converting values. The SQL standard has an abbreviation AT LOCAL to indicate the current time zone (defined by the <em>timezone<\/em> parameter value). Now AT LOCAL can be used in PostgreSQL. The following two expressions for the &#8216;Europe\/Moscow&#8217; timezone are now equivalent:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT  now() AT TIME ZONE 'Europe\/Moscow',         now() AT LOCAL \\gx<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">-[ RECORD 1 ]------------------------ timezone | 2023-12-18 12:57:29.612578 timezone | 2023-12-18 12:57:29.612578<\/code><\/pre>\n<p>  <a name=\"commit_519fc1bd\"><\/a>  <\/p>\n<h2 id=\"infinite-intervalshttpscommitfestpostgresqlorg454127\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4127\/\"><strong>Infinite intervals<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/519fc1bd\">519fc1bd<\/a><\/p>\n<p>  <\/p>\n<p>The type <code>interval<\/code> now works with infinite values:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT 'infinity'::interval, '-infinity'::interval;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> interval | interval   ----------+-----------  infinity | -infinity<\/code><\/pre>\n<p>  <\/p>\n<p>This allows for some new arithmetic operations:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT now() + 'infinity'::interval,        now() - 'infinity'::interval;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> ?column? | ?column?   ----------+-----------  infinity | -infinity<\/code><\/pre>\n<p>  <a name=\"commit_2d870b4a\"><\/a>  <\/p>\n<h2 id=\"alter-system-with-unrecognized-custom-parametershttpscommitfestpostgresqlorg454614\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4614\/\"><strong>ALTER SYSTEM with unrecognized custom parameters<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/2d870b4a\">2d870b4a<\/a><\/p>\n<p>  <\/p>\n<p>The ALTER SYSTEM command now can write custom parameters to <code>postgresql.auto.conf<\/code>:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">=# ALTER SYSTEM SET myapp.today = '2023-12-06'; ALTER SYSTEM<\/code><\/pre>\n<p>  <\/p>\n<p>By default, only the superuser can do this, but the privileges for specific parameters can be granted with the <code>GRANT.. ON PARAMETER<\/code> command.<\/p>\n<p>  <\/p>\n<p>In fact, ALTER SYSTEM has been capable of working with custom parameters even in previous versions, but they had to be declared in-session with the SET command and stored in the hash table in memory. This strange behavior was declared a mistake and is now fixed.<\/p>\n<p>  <\/p>\n<p>Again, you can still set custom parameters in the main configuration file <code>postgresql.conf<\/code>.<\/p>\n<p>  <a name=\"commit_721856ff\"><\/a>  <\/p>\n<h2 id=\"building-the-server-from-sourcehttpscommitfestpostgresqlorg454357\"><a href=\"https:\/\/commitfest.postgresql.org\/45\/4357\/\"><strong>Building the server from source<\/strong><\/a><\/h2>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/721856ff\">721856ff<\/a><\/p>\n<p>  <\/p>\n<p>Tar archives with source code will no longer contain files pre-generated with Flex, Bison, and perl utilities. There will also be no generated documentation files and man pages. Therefore, the build from the source code obtained from the <a href=\"https:\/\/www.postgresql.org\/ftp\/source\/\">Downloads<\/a> section will be identical to the build from the git repository source code. This means that in any case, flex, bison and perl will be required.<\/p>\n<p>  <\/p>\n<p>This was done primarily for the sake of the assembly system meson, because it can&#8217;t compile a server from tar archives as they exist now.<\/p>\n<p>  <\/p>\n<hr\/>\n<p>  <\/p>\n<p>That&#8217;s all I&#8217;ve got in 2023. Looking forward to the next PostgreSQL 17 commitfest in January!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/786658\/\"> https:\/\/habr.com\/ru\/articles\/786658\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ua\/k3\/zc\/uak3zcegyyo1tirna66hij1f_44.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ua\/k3\/zc\/uak3zcegyyo1tirna66hij1f_44.png\"\/><\/p>\n<p>  <\/p>\n<p>The November commitfest is ripe with new interesting features! Without further ado, let&#8217;s proceed with the review.<\/p>\n<p>  <\/p>\n<p>If you missed our July and September commitfest reviews, you can check them out here: <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-07<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-09<\/a>.<\/p>\n<p>  <\/p>\n<p><a href=\"#commit_e83d1b0c\">ON LOGIN trigger<\/a><br \/>  <a href=\"#commit_f21848de\">Event triggers for REINDEX<\/a><br \/>  <a href=\"#commit_2b5154be\">ALTER OPERATOR: commutator, negator, hashes, merges<\/a><br \/>  <a href=\"#commit_a5cf808b\">pg_dump &#8212;filter=dump.txt<\/a><br \/>  <a href=\"#commit_d1379ebf\">psql: displaying default privileges<\/a><br \/>  <a href=\"#commit_dc9f8a79\">pg_stat_statements: track statement entry timestamps and reset min\/max statistics<\/a><br \/>  <a href=\"#commit_96f05261\">pg_stat_checkpointer: checkpointer process statistics<\/a><br \/>  <a href=\"#commit_bc3c8db8\">pg_stats: statistics for range type columns<\/a><br \/>  <a href=\"#commit_d3d55ce5\">Planner: exclusion of unnecessary table self-joins<\/a><br \/>  <a href=\"#commit_f7816aec\">Planner: materialized CTE statistics<\/a><br \/>  <a href=\"#commit_5d8aa8bc\">Planner: accessing a table with multiple clauses<\/a><br \/>  <a href=\"#commit_e0b1ee17\">Index range scan optimization<\/a><br \/>  <a href=\"#commit_c789f0f6\">dblink, postgres_fdw: detailed wait events<\/a><br \/>  <a href=\"#commit_29d0a77f\">Logical replication: migration of replication slots during publisher upgrade<\/a><br \/>  <a href=\"#commit_7c3fb505\">Replication slot use log<\/a><br \/>  <a href=\"#commit_a02b37fc\">Unicode: new information functions<\/a><br \/>  <a href=\"#commit_526fe0d7\">New function: xmltext<\/a><br \/>  <a href=\"#commit_97957fdb\">AT LOCAL support<\/a><br \/>  <a href=\"#commit_519fc1bd\">Infinite intervals<\/a><br \/>  <a href=\"#commit_2d870b4a\">ALTER SYSTEM with unrecognized custom parameters<\/a><br \/>  <a href=\"#commit_721856ff\">Building the server from source<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-365404","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/365404","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=365404"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/365404\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=365404"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=365404"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=365404"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}