{"id":377383,"date":"2024-05-29T09:00:05","date_gmt":"2024-05-29T09:00:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=377383"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=377383","title":{"rendered":"<span>PostgreSQL 17: Part 4 or Commitfest 2024-01<\/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\/-g\/vv\/xn\/-gvvxngvuuxp-fcwmuptwyagwdy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/-g\/vv\/xn\/-gvvxngvuuxp-fcwmuptwyagwdy.png\"\/><\/p>\n<p>  <\/p>\n<p>Spring is in full swing as we bring you the hottest winter news of the January Commitfest. Let&#8217;s get to the good stuff right away!<\/p>\n<p>  <\/p>\n<p>Previous articles about PostgreSQL 17: <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-07<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970391\">2023-09<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970508\">2023-11<\/a>.<\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p><a href=\"#commit_174c4805\">Incremental backup<\/a><br \/>  <a href=\"#commit_9a17be1e\">Logical replication: maintaining the subscription status when upgrading the subscriber server<\/a><br \/>  <a href=\"#commit_8b2bcf3f\">Dynamic shared memory registry<\/a><br \/>  <a href=\"#commit_5de890e3\">EXPLAIN (memory): report memory usage for planning<\/a><br \/>  <a href=\"#commit_12915a58\">pg_stat_checkpointer: restartpoint monitoring on replicas<\/a><br \/>  <a href=\"#commit_b4375717\">Building BRIN indexes in parallel mode<\/a><br \/>  <a href=\"#commit_b262ad44\">Queries with the IS [NOT] NULL condition for NOT NULL columns<\/a><br \/>  <a href=\"#commit_ad57c2a7\">Optimization of SET search_path<\/a><br \/>  <a href=\"#commit_0452b461\">GROUP BY optimization<\/a><br \/>  <a href=\"#commit_075df6b2\">Support planner functions for range types<\/a><br \/>  <a href=\"#commit_5e8674dc\">PL\/pgSQL: %TYPE and %ROWTYPE arrays<\/a><br \/>  <a href=\"#commit_66ea94e8\">sonpath: new data conversion methods<\/a><br \/>  <a href=\"#commit_9e2d8701\">COPY\u2026 FROM: ignoring format conversion errors<\/a><br \/>  <a href=\"#commit_8ba6fdf9\">to_timestamp: format codes TZ and OF<\/a><br \/>  <a href=\"#commit_69958631\">GENERATED AS IDENTITY in partitioned tables<\/a><br \/>  <a href=\"#commit_5d06e99a\">ALTER COLUMN\u2026 SET EXPRESSION<\/a><\/p>\n<p>  <a name=\"commit_174c4805\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4648\/\">Incremental backup<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/174c4805\">174c4805<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/dc212340\">dc212340<\/a><\/p>\n<p>  <\/p>\n<p>To quickly restore to a point in time in the past, backups should be made frequently so that there are fewer WAL segments to go through to get to the desired point. However, full backups of large databases take up a lot of space, take a long time to create, and create unwanted load on the system.<\/p>\n<p>  <\/p>\n<p>PostgreSQL 17 solves this with <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/continuous-archiving.html#BACKUP-INCREMENTAL-BACKUP\">incremental backups<\/a>, which save only changes made relative to another backup.<\/p>\n<p>  <\/p>\n<p>Here&#8217;s how it works.<\/p>\n<p>  <\/p>\n<p>It all starts with walsummarizer, a process that scans WALs and collects information about modified pages. The process needs the <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/runtime-config-wal.html#GUC-SUMMARIZE-WAL\">summarize_wal<\/a> parameter to be switched on. No restart is required to change the parameter value, just a configuration reload. You can also run the process on your replicas, not just on the primary machine.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">ALTER SYSTEM SET summarize_wal = on; SELECT pg_reload_conf();<\/code><\/pre>\n<p>  <\/p>\n<p>The new process appears in pg_stat_activity and starts recording page changes in pg_wal\/summaries in a compact form.<\/p>\n<p>  <\/p>\n<p>First, create a full backup.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_basebackup -c fast -D backups\/full<\/code><\/pre>\n<p>  <\/p>\n<p>Now, introduce some changes; for example, make a copy of a table.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE TABLE tickets_copy AS SELECT * FROM tickets;<\/code><\/pre>\n<p>  <\/p>\n<p>Next, create an incremental backup relative to the full one. To do this, specify the full backup manifest file in the -i parameter.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_basebackup -c fast -D backups\/increment1 \\     -i backups\/full\/backup_manifest<\/code><\/pre>\n<p>  <\/p>\n<p>Make some more changes.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">DELETE FROM tickets_copy;<\/code><\/pre>\n<p>  <\/p>\n<p>Create another incremental backup that will be based on the last incremental backup, not on the full one. Specify the incremental backup manifest file with the -i parameter.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_basebackup -c fast -D backups\/increment2 \\     -i backups\/increment1\/backup_manifest<\/code><\/pre>\n<p>  <\/p>\n<p>To recover from an incremental backup, first, you need to reconstruct the full backup using the pg_combinebackup tool. Here, to recover from the second incremental backup, we need to specify the two incremental backups, the full backup, and a location for the new reconstructed full backup.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_combinebackup backups\/full backups\/increment1 backups\/increment2 \\     -o backups\/full_combined<\/code><\/pre>\n<p>  <\/p>\n<p>Let&#8217;s look at the backup sizes. As expected, incremental copies are significantly smaller than full ones.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ du -h -s backups\/*<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">2,7G    backups\/full 2,7G    backups\/full_combined 411M    backups\/increment1 25M     backups\/increment2<\/code><\/pre>\n<p>  <\/p>\n<p>Start the server from the new full backup on a free port:<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ pg_ctl start -D backups\/full_combined \\     -o '-p 5400' \\     -l backups\/full_combined\/logfile<\/code><\/pre>\n<p>  <\/p>\n<p>Confirm that the changes from the second incremental backup have been restored.<\/p>\n<p>  <\/p>\n<pre><code class=\"bash\">$ psql -p 5400 -c 'SELECT count(*) FROM tickets_copy'<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> count -------      0 (1 row)<\/code><\/pre>\n<p>  <\/p>\n<p>Let&#8217;s summarize. The server architecture offers two new components to support incremental backup.<\/p>\n<p>  <\/p>\n<ul>\n<li>The walsummarizer process, controlled by the <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/runtime-config-wal.html#GUC-SUMMARIZE-WAL\">summarize_wal<\/a> parameter, collects information about modified pages.<\/li>\n<li>The <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/app-pgwalsummary.html\">pg_walsummary<\/a> tool and the SQL functions pg_available_wal_summaries, pg_wal_summary_contents and pg_get_wal_summarizer_state serve as monitoring and diagnostics tools for the walsummarizer process and the contents of the pg_wal\/summaries directory.<\/li>\n<li>The <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/protocol-replication.html\">replication protocol<\/a> is updated with the UPLOAD_MANIFEST command, and the BASE_BACKUP command gets the INCREMENTAL parameter.<\/li>\n<li><a href=\"https:\/\/www.postgresql.org\/docs\/devel\/app-pgbasebackup.html\">pg_basebackup<\/a> gets the -i (&#8212;incremental) parameter that lets it create incremental backups using the updated replication protocol.<\/li>\n<li>The <a href=\"https:\/\/www.postgresql.org\/docs\/devel\/app-pgcombinebackup.html\">pg_combinebackup<\/a> tool reconstructs a full backup from an incremental copy and all copies on which it depends.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>See also: <a href=\"http:\/\/rhaas.blogspot.com\/2024\/01\/incremental-backup-what-to-copy.html\">Incremental Backup: What To Copy?<\/a> (Robert Haas)<br \/>  <a href=\"http:\/\/rhaas.blogspot.com\/2024\/01\/incremental-backups-evergreen-and-other.html\">Incremental Backups: Evergreen and Other Use Cases<\/a> (Robert Haas)<br \/>  <a href=\"https:\/\/pganalyze.com\/blog\/5mins-postgres-17-incremental-backups\">Waiting for Postgres 17: Incremental base backups<\/a> (Lukas Fittl)<br \/>  <a href=\"https:\/\/www.depesz.com\/2024\/01\/08\/waiting-for-postgresql-17-add-support-for-incremental-backup\/\">Waiting for PostgreSQL 17 \u2014 Add support for incremental backup.<\/a> (Hubert &#8216;depesz&#8217; Lubaczewski)<\/p>\n<p>  <a name=\"commit_9a17be1e\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4199\/\">Logical replication: maintaining the subscription status when upgrading the subscriber server<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/9a17be1e\">9a17be1e<\/a><\/p>\n<p>  <\/p>\n<p>The previous article explained <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970508#commit_29d0a77f\">migrating replication slots<\/a> on the publishing server during an upgrade to a new major version. This is another patch concerning upgrading the server in the context of logical replication. Now, when upgrading the subscriber server, the status of subscriptions is preserved, which will allow the subscriber to continue receiving changes from the publisher without re-synchronizing data.<\/p>\n<p>  <a name=\"commit_8b2bcf3f\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4684\/\">Dynamic shared memory registry<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/8b2bcf3f\">8b2bcf3f<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/abb0b4fc\">abb0b4fc<\/a><\/p>\n<p>  <\/p>\n<p>To be able to use shared memory, modules and libraries usually need to be loaded via <em>shared_preload_libraries<\/em>, and this requires restarting the server. Processes can be allocated dynamic shared memory segments (via the DSM API), but in order for other processes to access these, they need to know where to look. This is where the patch comes in: it creates and maintains a registry of dynamic shared memory segments (first commit).<\/p>\n<p>  <\/p>\n<p>The first extension to use the new interface is pg_prewarm (second commit). Calling the autoprewarm_start_worker and autoprewarm_dump_now functions no longer overconsumes shared memory if the pg_prewarn library was not loaded when the server started.<\/p>\n<p>  <a name=\"commit_5de890e3\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4492\/\">EXPLAIN (memory): report memory usage for planning<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/5de890e3\">5de890e3<\/a><\/p>\n<p>  <\/p>\n<p>PostgreSQL needs memory not only to execute queries, but also to plan them. The new <code>memory<\/code> parameter for EXPLAIN shows how much memory was used for constructing the plan.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">EXPLAIN (memory, costs off) SELECT * FROM tickets;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                   QUERY PLAN                     -------------------------------------------------  Seq Scan on tickets  Planning:    Memory: used=7920 bytes  allocated=8192 bytes (3 rows)<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"pgsql\">EXPLAIN (memory, costs off) SELECT * FROM tickets a, tickets b;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                    QUERY PLAN                      ---------------------------------------------------  Nested Loop    ->  Seq Scan on tickets a    ->  Materialize          ->  Seq Scan on tickets b  Planning:    Memory: used=19392 bytes  allocated=65536 bytes (6 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>The more tables involved in the query, the more memory is needed to create a plan. This is especially true for partitioned tables.<\/p>\n<p>  <a name=\"commit_12915a58\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/3961\/\">pg_stat_checkpointer: restartpoint monitoring on replicas<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/12915a58\">12915a58<\/a><\/p>\n<p>  <\/p>\n<p><a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970508#commit_96f05261\">pg_stat_checkpointer<\/a>, a new view in PostgreSQL 17, gets columns for restartpoint monitoring: restartpoints_timed, restartpoints_req and restartpoints_done. The last column shows how many restartpoints were actually made. This is needed because restartpoints on a replica cannot be made more often than those on the main server. Therefore, until the replica receives a WAL record saying that a restartpoint was made on the main server, an attempt to make a checkpoint on the replica will fail.<\/p>\n<p>  <a name=\"commit_b4375717\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4350\/\">Building BRIN indexes in parallel mode<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/b4375717\">b4375717<\/a><\/p>\n<p>  <\/p>\n<p>The patch enables building BRIN indexes by multiple worker processes in parallel. Previously, this was possible only for B-tree indexes.<\/p>\n<p>  <a name=\"commit_b262ad44\"><\/a>  <\/p>\n<p><a href=\"https:\/\/commitfest.postgresql.org\/46\/4459\/\"><strong>Queries with the IS [NOT] NULL condition for NOT NULL columns<\/strong><\/a><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/b262ad44\">b262ad44<\/a><\/p>\n<p>  <\/p>\n<p>In PostgreSQL 16, for each row of the tickets table, a check is performed to ensure that the ticket_no column is not empty:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (costs off) SELECT * FROM tickets WHERE ticket_no IS NOT NULL;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">            QUERY PLAN              -----------------------------------  Seq Scan on tickets    Filter: (ticket_no IS NOT NULL) (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>However, the ticket_no column has a NOT NULL constraint, so it cannot be empty, and checking it again is a waste of resources. It gets even worse with the IS NULL condition: the whole table has to be scanned (by index, but nevertheless) just to confirm that there&#8217;s nothing to return.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (costs off) SELECT * FROM tickets WHERE ticket_no IS NULL;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                QUERY PLAN                 ------------------------------------------  Index Scan using tickets_pkey on tickets    Index Cond: (ticket_no IS NULL) (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>Of course, you shouldn&#8217;t use such conditions in a query. But nothing says that you couldn&#8217;t.<\/p>\n<p>  <\/p>\n<p>Moreover, in some cases, when optimizing min\/max aggregates, the planner itself may include the IS NOT NULL condition into the query when rewriting it. This, in turn, may lead to poor index selection. There are more details on that in the patch discussion.<\/p>\n<p>  <\/p>\n<p>In PostgreSQL 17, to improve planning when IS [NOT] NULL conditions are involved, the NOT NULL condition check for the column is performed first.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# EXPLAIN (costs off) SELECT * FROM bookings WHERE book_date IS NOT NULL;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">      QUERY PLAN       ----------------------  Seq Scan on bookings (1 row)<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# EXPLAIN (costs off) SELECT * FROM tickets WHERE ticket_no IS NULL;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">        QUERY PLAN         --------------------------  Result    One-Time Filter: false (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>Note that the constraint is specifically <code>NOT NULL<\/code>. The constraint <code>CHECK (column_name IS NOT NULL)<\/code> performs the same check, but will not be taken into account by the planner.<\/p>\n<p>  <a name=\"commit_ad57c2a7\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4466\/\">Optimization of SET search_path<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/ad57c2a7\">ad57c2a7<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/a86c61c9\">a86c61c9<\/a><\/p>\n<p>  <\/p>\n<p>The function definition parameter <em>search_path<\/em> ensures safer operation with database objects. However, this comes at a cost.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s run some simple functions in psql and time them. First, without setting the parameter:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# CREATE FUNCTION f() RETURNS int AS 'SELECT 0' LANGUAGE SQL; 16=# \\timing on 16=# DO 'BEGIN PERFORM f() FROM generate_series(1, 10_000_000); END';<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">DO Time: 1909,958 ms (00:01,910)<\/code><\/pre>\n<p>  <\/p>\n<p>Now with the parameter set:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# ALTER FUNCTION f SET search_path = a,b,c; 16=# DO 'BEGIN PERFORM f() FROM generate_series(1, 10_000_000); END';<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">DO Time: 12594,300 ms (00:12,594)<\/code><\/pre>\n<p>  <\/p>\n<p>In PostgreSQL 17, <em>search_path<\/em> has been optimized by implementing a hash table of last used values, which are checked against during function calls. This has sped up the last test query:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# DO 'BEGIN PERFORM f() FROM generate_series(1, 10_000_000); END';<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">DO Time: 9501,717 ms (00:09,502)<\/code><\/pre>\n<p>  <a name=\"commit_0452b461\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4562\/\">GROUP BY optimization<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/0452b461\">0452b461<\/a><\/p>\n<p>  <\/p>\n<p>This optimization almost premiered in PostgreSQL 15, but was rolled back shortly before release. This is the second attempt.<\/p>\n<p>  <\/p>\n<p>In short, in queries where several columns are listed in GROUP BY, the planner can swap columns around in search of the optimal plan, for example, to use an index or incremental sorting. The output is unaffected, but performance may increase.<\/p>\n<p>  <\/p>\n<p>See also: <a href=\"https:\/\/blog.anayrat.info\/en\/2024\/01\/26\/group-by-reordering\/\">GROUP BY reordering<\/a> (Adrien Nayrat)<\/p>\n<p>  <a name=\"commit_075df6b2\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4656\/\">Support planner functions for range types<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/075df6b2\">075df6b2<\/a><\/p>\n<p>  <\/p>\n<p>Suppose we need to find out which flights were made in the first week of August 2017:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">16=# EXPLAIN (costs off) SELECT * FROM flights WHERE actual_departure &lt;@ tstzrange('2017-08-01', '2017-08-08', '[)');<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                            QUERY PLAN                                             --------------------------------------------------------------------------------------------------  Seq Scan on flights    Filter: (actual_departure &lt;@ '[\"2017-08-01 00:00:00+03\",\"2017-08-08 00:00:00+03\")'::tstzrange) (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>Note that the actual_departure column has an index, but it cannot be used with the &lt;@ operator.<\/p>\n<p>  <\/p>\n<p>In PostgreSQL 17, the planner gets support functions for range type operators @> and &lt;@. These functions rewrite the comparison so that it checks both boundaries of the range.<\/p>\n<p>  <\/p>\n<p>Here&#8217;s the plan of the same query after the patch:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">17=# EXPLAIN (costs off) SELECT * FROM flights WHERE actual_departure &lt;@ tstzrange('2017-08-01', '2017-08-08', '[)');<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">                                                                              QUERY PLAN                                                                               ----------------------------------------------------------------------------------------------------------------------------------------------------------------------  Index Scan using flights_actual_departure_idx on flights    Index Cond: ((actual_departure >= '2017-08-01 00:00:00+03'::timestamp with time zone) AND (actual_departure &lt; '2017-08-08 00:00:00+03'::timestamp with time zone)) (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>This way, the index is used as intended.<\/p>\n<p>  <a name=\"commit_5e8674dc\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4613\/\">PL\/pgSQL: %TYPE and %ROWTYPE arrays<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/5e8674dc\">5e8674dc<\/a><\/p>\n<p>  <\/p>\n<p>PL\/pgSQL now supports arrays with inheritance constructs of the %TYPE and %ROWTYPE types.<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">DO $$ DECLARE     seats_list seats.seat_no%TYPE[] :=         (SELECT array_agg(seat_no)          FROM seats          WHERE aircraft_code = '733' AND          fare_conditions = 'Business'); BEGIN     RAISE NOTICE '%', seats_list; END; $$ LANGUAGE plpgsql;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">NOTICE:  {1A,1C,1D,1F,2A,2C,2D,2F,3A,3C,3D,3F} DO<\/code><\/pre>\n<p>  <\/p>\n<p>Similarly, you can create a composite type array <code>table_name%ROWTYPE[]<\/code>.<\/p>\n<p>  <\/p>\n<p>See also: <a href=\"https:\/\/www.depesz.com\/2024\/01\/22\/waiting-for-postgresql-17-in-plpgsql-allow-type-and-rowtype-to-be-followed-by-array-decoration\/\">Waiting for PostgreSQL 17 \u2014 In plpgsql, allow %TYPE and %ROWTYPE to be followed by array decoration.<\/a> (Hubert &#8216;depesz&#8217; Lubaczewski)<\/p>\n<p>  <a name=\"commit_66ea94e8\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4526\/\">jsonpath: new data conversion methods<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/66ea94e8\">66ea94e8<\/a><\/p>\n<p>  <\/p>\n<p>Support for new data conversion methods has been added to the jsonpath language in accordance with the SQL standard: .bigint(), .boolean(), .date(), .decimal(), .integer(), .number(), .string(), .time(), .time_tz(), .timestamp(), .timestamp_tz().<\/p>\n<p>  <\/p>\n<p>Some usage examples:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">\\set json '{\"key1\": 42, \"key2\": \"t\", \"key3\": \"Hello, World!\"}'  SELECT jsonb_path_query(:'json', '$.key1.integer()'),        jsonb_path_query(:'json', '$.key2.boolean()'),        jsonb_path_query(:'json', '$.key3.string()');<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> jsonb_path_query | jsonb_path_query | jsonb_path_query ------------------+------------------+------------------  42               | true             | \"Hello, World!\"<\/code><\/pre>\n<p>  <a name=\"commit_9e2d8701\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/3817\/\">COPY\u2026 FROM: ignoring format conversion errors<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/9e2d8701\">9e2d8701<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/b725b7ee\">b725b7ee<\/a>, <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/72943960\">72943960<\/a><\/p>\n<p>  <\/p>\n<p>If an error loading any line occurs during COPY\u2026 FROM execution, the entire transaction is rolled back. It would be nice to be able to load all the correct rows and then process the ones with errors separately.<\/p>\n<p>  <\/p>\n<p>The first step has been taken in this direction. The COPY\u2026 FROM command now can ignore errors related to incorrect value formats in columns. This was based on the <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970086\">&#171;soft&#187; error handling feature<\/a> introduced in PostgreSQL 16.<\/p>\n<p>  <\/p>\n<p>Here&#8217;s an example table:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE TABLE t (id int PRIMARY KEY);<\/code><\/pre>\n<p>  <\/p>\n<p>The patch gives the COPY command a new parameter <code>on_error<\/code>: The default value <code>stop<\/code> makes it behave like it did before, stopping after the first error. The second and so far last value <code>ignore<\/code> makes the command ignore format conversion errors:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">COPY t FROM STDIN (on_error 'ignore'); Enter data to be copied followed by a newline. End with a backslash and a period on a line by itself, or an EOF signal. >> 1 >> two >> 3 >> \\.<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">NOTICE:  1 row was skipped due to data type incompatibility COPY 2<\/code><\/pre>\n<p>  <\/p>\n<p>Two of the three rows were loaded. One was not, hence the warning. If the process takes a while, the number of skipped rows can be monitored in the tuples_skipped column of the pg_stat_progress_copy view (the third commit).<\/p>\n<p>  <\/p>\n<p>How do we find out which rows were skipped? In the future, the parameter <code>on_error<\/code> will get values <code>file<\/code> and <code>table<\/code> to specify the name of the file or table.<\/p>\n<p>  <\/p>\n<p>Note that only format conversion errors will be ignored. Any other row load errors will still interrupt the process. For example, the first row here leads to a primary key integrity error, which is not allowed:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">COPY t FROM STDIN (on_error 'ignore'); Enter data to be copied followed by a newline. End with a backslash and a period on a line by itself, or an EOF signal. >> 3 >> 4 >> \\.<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">ERROR:  duplicate key value violates unique constraint \"t_pkey\" DETAIL:  Key (id)=(3) already exists. CONTEXT:  COPY t, line 1<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT * FROM t;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> id ----   1   3 (2 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>See also: <a href=\"https:\/\/www.depesz.com\/2024\/02\/07\/waiting-for-postgresql-17-add-new-copy-option-save_error_to-rename-copy-option-from-save_error_to-to-on_error\/\">Waiting for PostgreSQL 17 \u2014 Add new COPY option SAVE_ERROR_TO \/ Rename COPY option from SAVE_ERROR_TO to ON_ERROR<\/a> (Hubert &#8216;depesz&#8217; Lubaczewski)<\/p>\n<p>  <a name=\"commit_8ba6fdf9\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4362\/\">to_timestamp: format codes TZ and OF<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/8ba6fdf9\">8ba6fdf9<\/a><\/p>\n<p>  <\/p>\n<p>The function to_char has learned to understand the codes TZ (abbreviation from `time zone`) and OF (time zone offset from UTC) a while back.<\/p>\n<p>  <\/p>\n<p>The patch adds the support for these format codes to the reverse conversion function to_timestamp:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT to_timestamp('2024-02-18 23:50:00MSK', 'YYYY-MM-DD HH24:MI:SSTZ') tz,        to_timestamp('2024-02-18 23:50:00+03', 'YYYY-MM-DD HH24:MI:SSOF') of;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\">           tz           |           of            ------------------------+------------------------  2024-02-18 23:50:00+03 | 2024-02-18 23:50:00+03<\/code><\/pre>\n<p>  <a name=\"commit_69958631\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4737\/\">GENERATED AS IDENTITY in partitioned tables<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/69958631\">69958631<\/a><\/p>\n<p>  <\/p>\n<p>Identity columns declared as GENERATED AS IDENTITY are fully supported in partitioned tables.<\/p>\n<p>  <\/p>\n<p>When creating a new partition or attaching a partition from an existing table (ATTACH PARTITION), the identity column is associated with a specific sequence defined for the partitioned table. New values will be selected from the sequence both when inserted into a separate partition and into the partitioned table itself. When a partition is detached (DETACH PARTITION), the column of the now independent table is detached from the sequence.<\/p>\n<p>  <\/p>\n<p>See also: <a href=\"https:\/\/www.depesz.com\/2024\/02\/07\/waiting-for-postgresql-17-support-identity-columns-in-partitioned-tables\/\">Waiting for PostgreSQL 17 \u2014 Support identity columns in partitioned tables<\/a> (Hubert &#8216;depesz&#8217; Lubaczewski)<\/p>\n<p>  <a name=\"commit_5d06e99a\"><\/a>  <\/p>\n<p><strong><a href=\"https:\/\/commitfest.postgresql.org\/46\/4473\/\">ALTER COLUMN\u2026 SET EXPRESSION<\/a><\/strong><\/p>\n<p>  <\/p>\n<p>commit: <a href=\"https:\/\/github.com\/postgres\/postgres\/commit\/5d06e99a\">5d06e99a<\/a><\/p>\n<p>  <\/p>\n<p>The patch makes it possible to change the generated table column expressions. Previously, the expressions could only be deleted.<\/p>\n<p>  <\/p>\n<p>As an example, let&#8217;s distribute the rows of the table into three nodes in the node column:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">CREATE TABLE t (     id int PRIMARY KEY,     node int GENERATED ALWAYS AS (mod(id, 3)) STORED );  WITH ins AS (     INSERT INTO t SELECT x FROM generate_series(1,100) AS x     RETURNING * ) SELECT node, count(*) FROM ins GROUP BY 1 ORDER BY 1;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> node | count ------+-------     0 |    33     1 |    34     2 |    33 (3 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>When changing the number of nodes, you can replace the expression with a single command:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">ALTER TABLE t ALTER COLUMN node SET EXPRESSION AS (mod(id, 4));<\/code><\/pre>\n<p>  <\/p>\n<p>Notably, the command not only sets a new expression, but also completely rewrites the table, updating existing values:<\/p>\n<p>  <\/p>\n<pre><code class=\"pgsql\">SELECT node, count(*) FROM t GROUP BY 1 ORDER BY 1;<\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"plaintext\"> node | count ------+-------     0 |    25     1 |    25     2 |    25     3 |    25 (4 rows)<\/code><\/pre>\n<p>  <\/p>\n<p>See also: <a href=\"https:\/\/www.depesz.com\/2024\/01\/15\/waiting-for-postgresql-17-alter-table-command-to-change-generation-expression\/\">Waiting for PostgreSQL 17 \u2014 ALTER TABLE command to change generation expression<\/a> (Hubert &#8216;depesz&#8217; Lubaczewski)<\/p>\n<p>  <\/p>\n<p>This is all for now. The news of the last March Commitfest is soon to follow.<\/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\/817895\/\"> https:\/\/habr.com\/ru\/articles\/817895\/<\/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\/-g\/vv\/xn\/-gvvxngvuuxp-fcwmuptwyagwdy.png\" data-src=\"https:\/\/habrastorage.org\/webt\/-g\/vv\/xn\/-gvvxngvuuxp-fcwmuptwyagwdy.png\"\/><\/p>\n<p>  <\/p>\n<p>Spring is in full swing as we bring you the hottest winter news of the January Commitfest. Let&#8217;s get to the good stuff right away!<\/p>\n<p>  <\/p>\n<p>Previous articles about PostgreSQL 17: <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970285\">2023-07<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970391\">2023-09<\/a>, <a href=\"https:\/\/postgrespro.com\/blog\/pgsql\/5970508\">2023-11<\/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-377383","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/377383","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=377383"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/377383\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=377383"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=377383"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=377383"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}