diff options
| author | David Rowley | 2024-09-24 06:03:40 +0000 |
|---|---|---|
| committer | David Rowley | 2024-09-24 06:03:40 +0000 |
| commit | 62ddf7ee9a399e0b9624412fc482ed7365e38958 (patch) | |
| tree | ec91dd830a7d16225679037575d4c714d1095c0f /src | |
| parent | bbba59e69a56e1622e270f5e47b402c3a904cefc (diff) | |
Add ONLY support for VACUUM and ANALYZE
Since autovacuum does not trigger an ANALYZE for partitioned tables,
users must perform these manually. However, performing a manual ANALYZE
on a partitioned table would always result in recursively analyzing each
partition and that could be undesirable as autovacuum takes care of that.
For partitioned tables that contain a large number of partitions, having
to analyze each partition could take an unreasonably long time, especially
so for tables with a large number of columns.
Here we allow the ONLY keyword to prefix the name of the table to allow
users to have ANALYZE skip processing partitions. This option can also
be used with VACUUM, but there is no work to do if VACUUM ONLY is used on
a partitioned table.
This commit also changes the behavior of VACUUM and ANALYZE for
inheritance parents. Previously inheritance child tables would not be
processed when operating on the parent. Now, by default we *do* operate
on the child tables. ONLY can be used to obtain the old behavior.
The release notes should note this as an incompatibility. The default
behavior has not changed for partitioned tables as these always
recursively processed the partitions.
Author: Michael Harris <harmic@gmail.com>
Discussion: https://postgr.es/m/CADofcAWATx_haD=QkSxHbnTsAe6+e0Aw8Eh4H8cXyogGvn_kOg@mail.gmail.com
Discussion: https://postgr.es/m/CADofcAXVbD0yGp_EaC9chmzsOoSai3jcfBCnyva3j0RRdRvMVA@mail.gmail.com
Reviewed-by: Jelte Fennema-Nio <postgres@jeltef.nl>
Reviewed-by: Melih Mutlu <m.melihmutlu@gmail.com>
Reviewed-by: Atsushi Torikoshi <torikoshia@oss.nttdata.com>
Reviewed-by: jian he <jian.universality@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/backend/commands/vacuum.c | 39 | ||||
| -rw-r--r-- | src/backend/parser/gram.y | 2 | ||||
| -rw-r--r-- | src/test/regress/expected/vacuum.out | 92 | ||||
| -rw-r--r-- | src/test/regress/sql/vacuum.sql | 66 |
4 files changed, 186 insertions, 13 deletions
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 7d8e9d20454..23aabdc90dc 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -851,7 +851,7 @@ vacuum_open_relation(Oid relid, RangeVar *relation, bits32 options, /* * Given a VacuumRelation, fill in the table OID if it wasn't specified, - * and optionally add VacuumRelations for partitions of the table. + * and optionally add VacuumRelations for partitions or inheritance children. * * If a VacuumRelation does not have an OID supplied and is a partitioned * table, an extra entry will be added to the output for each partition. @@ -879,11 +879,15 @@ expand_vacuum_rel(VacuumRelation *vrel, MemoryContext vac_context, } else { - /* Process a specific relation, and possibly partitions thereof */ + /* + * Process a specific relation, and possibly partitions 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 +948,31 @@ 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 - * 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 - * 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. + * Unless the user has specified ONLY, make relation list entries for + * its partitions or inheritance child 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 or child tables yet, and doing so would just + * add unnecessary deadlock risk. For this last reason, we do not yet + * check 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 ab304ca989d..b1d4642c59b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11921,7 +11921,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 2eba7128876..1a07dbf67d6 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -291,6 +291,98 @@ ANALYZE vactst, vactst; BEGIN; -- ANALYZE behaves differently inside a transaction block ANALYZE vactst, vactst; COMMIT; +-- +-- Tests for ANALYZE ONLY / VACUUM ONLY on partitioned tables +-- +CREATE TABLE only_parted (a int, b text) PARTITION BY LIST (a); +CREATE TABLE only_parted1 PARTITION OF only_parted FOR VALUES IN (1); +INSERT INTO only_parted VALUES (1, 'a'); +-- Ensure only the partitioned table is analyzed +ANALYZE ONLY only_parted; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_parted'::regclass, 'only_parted1'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +--------------+----------+---------- + only_parted | t | f + only_parted1 | f | f +(2 rows) + +-- Ensure partitioned table and the partitions are analyzed +ANALYZE only_parted; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_parted'::regclass, 'only_parted1'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +--------------+----------+---------- + only_parted | t | f + only_parted1 | t | f +(2 rows) + +DROP TABLE only_parted; +-- VACUUM ONLY on a partitioned table does nothing, ensure we get a warning. +VACUUM ONLY vacparted; +WARNING: VACUUM ONLY of partitioned table "vacparted" has no effect +-- Try ANALYZE ONLY with a column list +ANALYZE ONLY vacparted(a,b); +-- +-- Tests for VACUUM ONLY / ANALYZE ONLY on inheritance 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'); +-- Ensure only parent is analyzed +ANALYZE ONLY only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +-----------------+----------+---------- + only_inh_child | f | f + only_inh_parent | t | f +(2 rows) + +-- Ensure the parent and child are analyzed +ANALYZE only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +-----------------+----------+---------- + only_inh_child | t | f + only_inh_parent | t | f +(2 rows) + +-- Ensure only the parent is vacuumed +VACUUM ONLY only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +-----------------+----------+---------- + only_inh_child | t | f + only_inh_parent | t | t +(2 rows) + +-- Ensure parent and child are vacuumed +VACUUM only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + relname | analyzed | vacuumed +-----------------+----------+---------- + only_inh_child | t | t + only_inh_parent | t | t +(2 rows) + +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 548cd7accac..5e55079e718 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -233,6 +233,72 @@ BEGIN; -- ANALYZE behaves differently inside a transaction block ANALYZE vactst, vactst; COMMIT; +-- +-- Tests for ANALYZE ONLY / VACUUM ONLY on partitioned tables +-- +CREATE TABLE only_parted (a int, b text) PARTITION BY LIST (a); +CREATE TABLE only_parted1 PARTITION OF only_parted FOR VALUES IN (1); +INSERT INTO only_parted VALUES (1, 'a'); + +-- Ensure only the partitioned table is analyzed +ANALYZE ONLY only_parted; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_parted'::regclass, 'only_parted1'::regclass) + ORDER BY relname; + +-- Ensure partitioned table and the partitions are analyzed +ANALYZE only_parted; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_parted'::regclass, 'only_parted1'::regclass) + ORDER BY relname; + +DROP TABLE only_parted; + +-- VACUUM ONLY on a partitioned table does nothing, ensure we get a warning. +VACUUM ONLY vacparted; + +-- Try ANALYZE ONLY with a column list +ANALYZE ONLY vacparted(a,b); + +-- +-- Tests for VACUUM ONLY / ANALYZE ONLY on inheritance 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'); + +-- Ensure only parent is analyzed +ANALYZE ONLY only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + +-- Ensure the parent and child are analyzed +ANALYZE only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + +-- Ensure only the parent is vacuumed +VACUUM ONLY only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + +-- Ensure parent and child are vacuumed +VACUUM only_inh_parent; +SELECT relname, last_analyze IS NOT NULL AS analyzed, last_vacuum IS NOT NULL AS vacuumed + FROM pg_stat_user_tables + WHERE relid IN ('only_inh_parent'::regclass, 'only_inh_child'::regclass) + ORDER BY relname; + +DROP TABLE only_inh_parent CASCADE; + -- parenthesized syntax for ANALYZE ANALYZE (VERBOSE) does_not_exist; ANALYZE (nonexistent-arg) does_not_exist; |
