diff options
-rw-r--r-- | doc/src/sgml/ddl.sgml | 23 | ||||
-rw-r--r-- | doc/src/sgml/monitoring.sgml | 6 | ||||
-rw-r--r-- | doc/src/sgml/ref/analyze.sgml | 36 | ||||
-rw-r--r-- | doc/src/sgml/ref/vacuum.sgml | 11 | ||||
-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 |
8 files changed, 230 insertions, 45 deletions
diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index b671858627b..8ab0ddb112f 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -3778,12 +3778,14 @@ VALUES ('Albany', NULL, NULL, 'NY'); not <literal>INSERT</literal> or <literal>ALTER TABLE ... RENAME</literal>) typically default to including child tables and support the <literal>ONLY</literal> notation to exclude them. - Commands that do database maintenance and tuning - (e.g., <literal>REINDEX</literal>, <literal>VACUUM</literal>) - typically only work on individual, physical tables and do not - support recursing over inheritance hierarchies. The respective - behavior of each individual command is documented in its reference - page (<xref linkend="sql-commands"/>). + The majority of commands that do database maintenance and tuning + (e.g., <literal>REINDEX</literal>) only work on individual, physical + tables and do not support recursing over inheritance hierarchies. + However, both <literal>VACUUM</literal> and <literal>ANALYZE</literal> + commands default to including child tables and the <literal>ONLY</literal> + notation is supported to allow them to be excluded. The respective + behavior of each individual command is documented in its reference page + (<xref linkend="sql-commands"/>). </para> <para> @@ -4854,11 +4856,12 @@ ALTER TABLE measurement_y2008m02 INHERIT measurement; <listitem> <para> - If you are using manual <command>VACUUM</command> or - <command>ANALYZE</command> commands, don't forget that - you need to run them on each child table individually. A command like: + Manual <command>VACUUM</command> and <command>ANALYZE</command> + commands will automatically process all inheritance child tables. If + this is undesirable, you can use the <literal>ONLY</literal> keyword. + A command like: <programlisting> -ANALYZE measurement; +ANALYZE ONLY measurement; </programlisting> will only process the root table. </para> diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index a2fda4677d7..db7f35a4515 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -5525,9 +5525,9 @@ FROM pg_stat_get_backend_idset() AS backendid; <note> <para> - Note that when <command>ANALYZE</command> is run on a partitioned table, - all of its partitions are also recursively analyzed. - In that case, <command>ANALYZE</command> + Note that when <command>ANALYZE</command> is run on a partitioned table + without the <literal>ONLY</literal> keyword, all of its partitions are + also recursively analyzed. In that case, <command>ANALYZE</command> progress is reported first for the parent table, whereby its inheritance statistics are collected, followed by that for each partition. </para> diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index 2b94b378e9f..a0db56ae743 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -31,7 +31,7 @@ ANALYZE [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase> - <replaceable class="parameter">table_name</replaceable> [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] + [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] </synopsis> </refsynopsisdiv> @@ -142,9 +142,12 @@ ANALYZE [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r The name (possibly schema-qualified) of a specific table to analyze. If omitted, all regular tables, partitioned tables, and materialized views in the current database are analyzed (but not - foreign tables). If the specified table is a partitioned table, both the - inheritance statistics of the partitioned table as a whole and - statistics of the individual partitions are updated. + foreign tables). If <literal>ONLY</literal> is specified before + the table name, only that table is analyzed. If <literal>ONLY</literal> + is not specified, the table and all its inheritance child tables or + partitions (if any) are analyzed. Optionally, <literal>*</literal> + can be specified after the table name to explicitly indicate that + inheritance child tables (or partitions) are to be analyzed. </para> </listitem> </varlistentry> @@ -284,22 +287,23 @@ ANALYZE [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <r <command>ANALYZE</command> gathers two sets of statistics: one on the rows of the parent table only, and a second including rows of both the parent table and all of its children. This second set of statistics is needed when - planning queries that process the inheritance tree as a whole. The child - tables themselves are not individually analyzed in this case. - The autovacuum daemon, however, will only consider inserts or - updates on the parent table itself when deciding whether to trigger an - automatic analyze for that table. If that table is rarely inserted into - or updated, the inheritance statistics will not be up to date unless you - run <command>ANALYZE</command> manually. + planning queries that process the inheritance tree as a whole. The + autovacuum daemon, however, will only consider inserts or updates on the + parent table itself when deciding whether to trigger an automatic analyze + for that table. If that table is rarely inserted into or updated, the + inheritance statistics will not be up to date unless you run + <command>ANALYZE</command> manually. By default, + <command>ANALYZE</command> will also recursively collect and update the + statistics for each inheritance child table. The <literal>ONLY</literal> + keyword may be used to disable this. </para> <para> For partitioned tables, <command>ANALYZE</command> gathers statistics by - sampling rows from all partitions; in addition, it will recurse into each - partition and update its statistics. Each leaf partition is analyzed only - once, even with multi-level partitioning. No statistics are collected for - only the parent table (without data from its partitions), because with - partitioning it's guaranteed to be empty. + sampling rows from all partitions. By default, + <command>ANALYZE</command> will also recursively collect and update the + statistics for each partition. The <literal>ONLY</literal> keyword may be + used to disable this. </para> <para> diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index 9857b35627b..9110938fab6 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -42,7 +42,7 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re <phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase> - <replaceable class="parameter">table_name</replaceable> [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] + [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * ] [ ( <replaceable class="parameter">column_name</replaceable> [, ...] ) ] </synopsis> </refsynopsisdiv> @@ -401,8 +401,13 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re <listitem> <para> 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 <literal>ONLY</literal> is specified + before the table name, only that table is vacuumed. If + <literal>ONLY</literal> is not specified, the table and all its + inheritance child tables or partitions (if any) are also vacuumed. + Optionally, <literal>*</literal> can be specified after the table name + to explicitly indicate that inheritance child tables (or partitions) are + to be vacuumed. </para> </listitem> </varlistentry> 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; |