From 6e8a1e103d87e5644fea0a547e8b23b77ff8f78d Mon Sep 17 00:00:00 2001 From: Michael Harris Date: Thu, 22 Aug 2024 18:45:07 +1000 Subject: [PATCH] Initial implementation of the 'ONLY' feature for ANALYZE and VACUUM. This patch also changes the behaivour of ANALYZE and VACUUM when given on parent tables in an inheritance structure, to align with the case of partitioned tables. In the absence of ONLY, descendant tables will be included. Refer to the thread at: https://www.postgresql.org/message-id/flat/CADofcAWATx_haD%3DQkSxHbnTsAe6%2Be0Aw8Eh4H8cXyogGvn_kOg%40mail.gmail.com --- compile_flags.txt | 1 + doc/src/sgml/ref/analyze.sgml | 11 +++-- doc/src/sgml/ref/vacuum.sgml | 10 +++-- src/backend/commands/vacuum.c | 30 ++++++++++---- src/backend/parser/gram.y | 2 +- src/test/regress/expected/vacuum.out | 62 ++++++++++++++++++++++++++++ src/test/regress/sql/vacuum.sql | 40 ++++++++++++++++++ 7 files changed, 140 insertions(+), 16 deletions(-) create mode 100644 compile_flags.txt diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000000..7297dbe134 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1 @@ +-Isrc/include/ diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index 2b94b378e9..9ed39b0206 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -ANALYZE [ ( option [, ...] ) ] [ table_and_columns [, ...] ] +ANALYZE [ ( option [, ...] ) ] [ [ ONLY ] table_and_columns [, ...] ] where option can be one of: @@ -142,9 +142,12 @@ ANALYZE [ ( option [, ...] ) ] [ ONLY is specified before + the table name, only that table is analyzed. If ONLY + is not specified, the table and all its descendant tables or partitions + (if any) are analyzed. Optionally, * + can be specified after the table name to explicitly indicate that + descendant tables are included. diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index 9857b35627..a0e062baa1 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ] +VACUUM [ ( option [, ...] ) ] [ [ ONLY ] table_and_columns [, ...] ] where option can be one of: @@ -401,8 +401,12 @@ VACUUM [ ( option [, ...] ) ] [ The name (optionally schema-qualified) of a specific table or - materialized view to vacuum. If the specified table is a partitioned - table, all of its leaf partitions are vacuumed. + materialized view to vacuum. If ONLY is specified before + the table name, only that table is vacuumed. If ONLY + is not specified, the table and all its descendant tables or partitions + (if any) are vacuumed. Optionally, * + can be specified after the table name to explicitly indicate that + descendant tables are included. diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 7d8e9d2045..cd37f90687 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -24,6 +24,7 @@ #include "postgres.h" #include +#include #include "access/clog.h" #include "access/commit_ts.h" @@ -879,11 +880,12 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, } else { - /* Process a specific relation, and possibly partitions thereof */ + /* Process a specific relation, and possibly partitions and/or child tables thereof */ Oid relid; HeapTuple tuple; Form_pg_class classForm; - bool include_parts; + bool include_children; + bool is_partitioned_table; int rvr_opts; /* @@ -944,20 +946,32 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, MemoryContextSwitchTo(oldcontext); } + /* + * Vacuuming a partitioned table with ONLY will not do anything + * since the partitioned table itself is empty. Issue a warning + * if the user requests this. + */ + include_children = vrel->relation->inh; + is_partitioned_table = (classForm->relkind == RELKIND_PARTITIONED_TABLE); + if ((options & VACOPT_VACUUM) && is_partitioned_table && ! include_children) + ereport(WARNING, + (errmsg("VACUUM ONLY of partitioned table \"%s\" has no effect", + vrel->relation->relname))); + - include_parts = (classForm->relkind == RELKIND_PARTITIONED_TABLE); ReleaseSysCache(tuple); /* - * If it is, make relation list entries for its partitions. Note that + * Unless the user has specified ONLY, make relation list entries for + * its partitions and/or descendant tables. Note that * the list returned by find_all_inheritors() includes the passed-in * OID, so we have to skip that. There's no point in taking locks on - * the individual partitions yet, and doing so would just add + * the individual partitions/tables yet, and doing so would just add * unnecessary deadlock risk. For this last reason we do not check - * yet the ownership of the partitions, which get added to the list to - * process. Ownership will be checked later on anyway. + * yet the ownership of the partitions/tables, which get added to the + * list to process. Ownership will be checked later on anyway. */ - if (include_parts) + if (include_children) { List *part_oids = find_all_inheritors(relid, NoLock, NULL); ListCell *part_lc; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index b7d98eb9f0..7cb3a9092a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11939,7 +11939,7 @@ opt_name_list: ; vacuum_relation: - qualified_name opt_name_list + relation_expr opt_name_list { $$ = (Node *) makeVacuumRelation($1, InvalidOid, $2); } diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index 2eba712887..e1827e6e10 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -291,6 +291,68 @@ ANALYZE vactst, vactst; BEGIN; -- ANALYZE behaves differently inside a transaction block ANALYZE vactst, vactst; COMMIT; +-- ANALYZE ONLY / VACUUM ONLY on partitioned table +CREATE TABLE only_parted (a int, b char) PARTITION BY LIST (a); +CREATE TABLE only_parted1 PARTITION OF vacparted FOR VALUES IN (1); +ERROR: partition "only_parted1" would overlap partition "vacparted1" +LINE 1: ...TABLE only_parted1 PARTITION OF vacparted FOR VALUES IN (1); + ^ +INSERT INTO only_parted1 VALUES (1, 'a'); +ERROR: relation "only_parted1" does not exist +LINE 1: INSERT INTO only_parted1 VALUES (1, 'a'); + ^ +-- Only partitioned table is analyzed +ANALYZE ONLY only_parted; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_parted%' + ORDER BY relname; + relname | ?column? | ?column? +-------------+----------+---------- + only_parted | t | t +(1 row) + +-- Partitioned table and partitions are analyzed +ANALYZE only_parted; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_parted%' + ORDER BY relname; + relname | ?column? | ?column? +-------------+----------+---------- + only_parted | t | t +(1 row) + +VACUUM ONLY vacparted; -- gives warning +WARNING: VACUUM ONLY of partitioned table "vacparted" has no effect +ANALYZE ONLY vacparted(a,b); -- combine ONLY with column list +-- ANALYZE ONLY on inherited tables +CREATE TABLE only_inh_parent (a int primary key, b TEXT); +CREATE TABLE only_inh_child () INHERITS (only_inh_parent); +INSERT INTO only_inh_child(a,b) VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc'); +-- Only parent is ANALYZED +ANALYZE ONLY only_inh_parent; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_inh%' + ORDER BY relname; + relname | ?column? | ?column? +-----------------+----------+---------- + only_inh_child | t | t + only_inh_parent | f | t +(2 rows) + +-- Parent and child is ANALYZED +ANALYZE only_inh_parent; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_inh%' + ORDER BY relname; + relname | ?column? | ?column? +-----------------+----------+---------- + only_inh_child | f | t + only_inh_parent | f | t +(2 rows) + +DROP TABLE only_parted CASCADE; +DROP TABLE only_inh_parent CASCADE; +NOTICE: drop cascades to table only_inh_child -- parenthesized syntax for ANALYZE ANALYZE (VERBOSE) does_not_exist; ERROR: relation "does_not_exist" does not exist diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index 548cd7acca..510991377a 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -233,6 +233,46 @@ BEGIN; -- ANALYZE behaves differently inside a transaction block ANALYZE vactst, vactst; COMMIT; +-- ANALYZE ONLY / VACUUM ONLY on partitioned table +CREATE TABLE only_parted (a int, b char) PARTITION BY LIST (a); +CREATE TABLE only_parted1 PARTITION OF vacparted FOR VALUES IN (1); +INSERT INTO only_parted1 VALUES (1, 'a'); + +-- Only partitioned table is analyzed +ANALYZE ONLY only_parted; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_parted%' + ORDER BY relname; + +-- Partitioned table and partitions are analyzed +ANALYZE only_parted; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_parted%' + ORDER BY relname; + +VACUUM ONLY vacparted; -- gives warning +ANALYZE ONLY vacparted(a,b); -- combine ONLY with column list + +-- ANALYZE ONLY on inherited tables +CREATE TABLE only_inh_parent (a int primary key, b TEXT); +CREATE TABLE only_inh_child () INHERITS (only_inh_parent); +INSERT INTO only_inh_child(a,b) VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc'); + +-- Only parent is ANALYZED +ANALYZE ONLY only_inh_parent; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_inh%' + ORDER BY relname; + +-- Parent and child is ANALYZED +ANALYZE only_inh_parent; +SELECT relname, last_analyze is null, last_vacuum is null FROM pg_stat_user_tables + WHERE relname like 'only_inh%' + ORDER BY relname; + +DROP TABLE only_parted CASCADE; +DROP TABLE only_inh_parent CASCADE; + -- parenthesized syntax for ANALYZE ANALYZE (VERBOSE) does_not_exist; ANALYZE (nonexistent-arg) does_not_exist; -- 2.31.1