mirror of
https://github.com/postgres/postgres.git
synced 2025-04-24 10:47:04 +03:00
Allow multiple tables to be specified in one VACUUM or ANALYZE command.
Not much to say about this; does what it says on the tin. However, formerly, if there was a column list then the ANALYZE action was implied; now it must be specified, or you get an error. This is because it would otherwise be a bit unclear what the user meant if some tables have column lists and some don't. Nathan Bossart, reviewed by Michael Paquier and Masahiko Sawada, with some editorialization by me Discussion: https://postgr.es/m/E061A8E3-5E3D-494D-94F0-E8A9B312BBFC@amazon.com
This commit is contained in:
parent
45f9d08684
commit
11d8d72c27
@ -21,7 +21,11 @@ PostgreSQL documentation
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
|
||||
ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
|
||||
|
||||
<phrase>where <replaceable class="PARAMETER">table_and_columns</replaceable> is:</phrase>
|
||||
|
||||
<replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -38,9 +42,11 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With no parameter, <command>ANALYZE</command> examines every table in the
|
||||
current database. With a parameter, <command>ANALYZE</command> examines
|
||||
only that table. It is further possible to give a list of column names,
|
||||
Without a <replaceable class="PARAMETER">table_and_columns</replaceable>
|
||||
list, <command>ANALYZE</command> processes every table and materialized view
|
||||
in the current database that the current user has permission to analyze.
|
||||
With a list, <command>ANALYZE</command> processes only those table(s).
|
||||
It is further possible to give a list of column names for a table,
|
||||
in which case only the statistics for those columns are collected.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
@ -21,9 +21,20 @@ PostgreSQL documentation
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>
|
||||
VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
|
||||
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> ]
|
||||
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ]
|
||||
VACUUM [ ( <replaceable class="PARAMETER">option</replaceable> [, ...] ) ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
|
||||
VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="PARAMETER">table_and_columns</replaceable> [, ...] ]
|
||||
|
||||
<phrase>where <replaceable class="PARAMETER">option</replaceable> can be one of:</phrase>
|
||||
|
||||
FULL
|
||||
FREEZE
|
||||
VERBOSE
|
||||
ANALYZE
|
||||
DISABLE_PAGE_SKIPPING
|
||||
|
||||
<phrase>and <replaceable class="PARAMETER">table_and_columns</replaceable> is:</phrase>
|
||||
|
||||
<replaceable class="PARAMETER">table_name</replaceable> [ ( <replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ]
|
||||
</synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
@ -40,9 +51,10 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
|
||||
</para>
|
||||
|
||||
<para>
|
||||
With no parameter, <command>VACUUM</command> processes every table in the
|
||||
current database that the current user has permission to vacuum.
|
||||
With a parameter, <command>VACUUM</command> processes only that table.
|
||||
Without a <replaceable class="PARAMETER">table_and_columns</replaceable>
|
||||
list, <command>VACUUM</command> processes every table and materialized view
|
||||
in the current database that the current user has permission to vacuum.
|
||||
With a list, <command>VACUUM</command> processes only those table(s).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -141,8 +153,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
|
||||
except when performing an aggressive vacuum, some pages may be skipped
|
||||
in order to avoid waiting for other sessions to finish using them.
|
||||
This option disables all page-skipping behavior, and is intended to
|
||||
be used only the contents of the visibility map are thought to
|
||||
be suspect, which should happen only if there is a hardware or software
|
||||
be used only when the contents of the visibility map are
|
||||
suspect, which should happen only if there is a hardware or software
|
||||
issue causing database corruption.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -152,9 +164,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
|
||||
<term><replaceable class="PARAMETER">table_name</replaceable></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The name (optionally schema-qualified) of a specific table to
|
||||
vacuum. If omitted, all regular tables and materialized views in the
|
||||
current database are vacuumed. If the specified table is a partitioned
|
||||
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.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -165,7 +176,8 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">
|
||||
<listitem>
|
||||
<para>
|
||||
The name of a specific column to analyze. Defaults to all columns.
|
||||
If a column list is specified, <literal>ANALYZE</> is implied.
|
||||
If a column list is specified, <literal>ANALYZE</> must also be
|
||||
specified.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "commands/cluster.h"
|
||||
#include "commands/vacuum.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "storage/bufmgr.h"
|
||||
@ -67,7 +68,8 @@ static BufferAccessStrategy vac_strategy;
|
||||
|
||||
|
||||
/* non-export function prototypes */
|
||||
static List *get_rel_oids(Oid relid, const RangeVar *vacrel);
|
||||
static List *expand_vacuum_rel(VacuumRelation *vrel);
|
||||
static List *get_all_vacuum_rels(void);
|
||||
static void vac_truncate_clog(TransactionId frozenXID,
|
||||
MultiXactId minMulti,
|
||||
TransactionId lastSaneFrozenXid,
|
||||
@ -90,9 +92,26 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
|
||||
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
|
||||
Assert((vacstmt->options & VACOPT_VACUUM) ||
|
||||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
|
||||
Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);
|
||||
Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
|
||||
|
||||
/*
|
||||
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
|
||||
*/
|
||||
if (!(vacstmt->options & VACOPT_ANALYZE))
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, vacstmt->rels)
|
||||
{
|
||||
VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
|
||||
|
||||
if (vrel->va_cols != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("ANALYZE option must be specified when a column list is provided")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* All freeze ages are zero if the FREEZE option is given; otherwise pass
|
||||
* them as -1 which means to use the default values.
|
||||
@ -119,26 +138,22 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
|
||||
params.log_min_duration = -1;
|
||||
|
||||
/* Now go through the common routine */
|
||||
vacuum(vacstmt->options, vacstmt->relation, InvalidOid, ¶ms,
|
||||
vacstmt->va_cols, NULL, isTopLevel);
|
||||
vacuum(vacstmt->options, vacstmt->rels, ¶ms, NULL, isTopLevel);
|
||||
}
|
||||
|
||||
/*
|
||||
* Primary entry point for VACUUM and ANALYZE commands.
|
||||
* Internal entry point for VACUUM and ANALYZE commands.
|
||||
*
|
||||
* options is a bitmask of VacuumOption flags, indicating what to do.
|
||||
*
|
||||
* relid, if not InvalidOid, indicates the relation to process; otherwise,
|
||||
* if a RangeVar is supplied, that's what to process; otherwise, we process
|
||||
* all relevant tables in the database. (If both relid and a RangeVar are
|
||||
* supplied, the relid is what is processed, but we use the RangeVar's name
|
||||
* to report any open/lock failure.)
|
||||
* relations, if not NIL, is a list of VacuumRelation to process; otherwise,
|
||||
* we process all relevant tables in the database. For each VacuumRelation,
|
||||
* if a valid OID is supplied, the table with that OID is what to process;
|
||||
* otherwise, the VacuumRelation's RangeVar indicates what to process.
|
||||
*
|
||||
* params contains a set of parameters that can be used to customize the
|
||||
* behavior.
|
||||
*
|
||||
* va_cols is a list of columns to analyze, or NIL to process them all.
|
||||
*
|
||||
* bstrategy is normally given as NULL, but in autovacuum it can be passed
|
||||
* in to use the same buffer strategy object across multiple vacuum() calls.
|
||||
*
|
||||
@ -148,14 +163,14 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
|
||||
* memory context that will not disappear at transaction commit.
|
||||
*/
|
||||
void
|
||||
vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
List *va_cols, BufferAccessStrategy bstrategy, bool isTopLevel)
|
||||
vacuum(int options, List *relations, VacuumParams *params,
|
||||
BufferAccessStrategy bstrategy, bool isTopLevel)
|
||||
{
|
||||
static bool in_vacuum = false;
|
||||
|
||||
const char *stmttype;
|
||||
volatile bool in_outer_xact,
|
||||
use_own_xacts;
|
||||
List *relations;
|
||||
static bool in_vacuum = false;
|
||||
|
||||
Assert(params != NULL);
|
||||
|
||||
@ -228,10 +243,29 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
vac_strategy = bstrategy;
|
||||
|
||||
/*
|
||||
* Build list of relation OID(s) to process, putting it in vac_context for
|
||||
* safekeeping.
|
||||
* Build list of relation(s) to process, putting any new data in
|
||||
* vac_context for safekeeping.
|
||||
*/
|
||||
relations = get_rel_oids(relid, relation);
|
||||
if (relations != NIL)
|
||||
{
|
||||
List *newrels = NIL;
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, relations)
|
||||
{
|
||||
VacuumRelation *vrel = lfirst_node(VacuumRelation, lc);
|
||||
List *sublist;
|
||||
MemoryContext old_context;
|
||||
|
||||
sublist = expand_vacuum_rel(vrel);
|
||||
old_context = MemoryContextSwitchTo(vac_context);
|
||||
newrels = list_concat(newrels, sublist);
|
||||
MemoryContextSwitchTo(old_context);
|
||||
}
|
||||
relations = newrels;
|
||||
}
|
||||
else
|
||||
relations = get_all_vacuum_rels();
|
||||
|
||||
/*
|
||||
* Decide whether we need to start/commit our own transactions.
|
||||
@ -282,7 +316,7 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
CommitTransactionCommand();
|
||||
}
|
||||
|
||||
/* Turn vacuum cost accounting on or off */
|
||||
/* Turn vacuum cost accounting on or off, and set/clear in_vacuum */
|
||||
PG_TRY();
|
||||
{
|
||||
ListCell *cur;
|
||||
@ -299,11 +333,11 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
*/
|
||||
foreach(cur, relations)
|
||||
{
|
||||
Oid relid = lfirst_oid(cur);
|
||||
VacuumRelation *vrel = lfirst_node(VacuumRelation, cur);
|
||||
|
||||
if (options & VACOPT_VACUUM)
|
||||
{
|
||||
if (!vacuum_rel(relid, relation, options, params))
|
||||
if (!vacuum_rel(vrel->oid, vrel->relation, options, params))
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -320,8 +354,8 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
PushActiveSnapshot(GetTransactionSnapshot());
|
||||
}
|
||||
|
||||
analyze_rel(relid, relation, options, params,
|
||||
va_cols, in_outer_xact, vac_strategy);
|
||||
analyze_rel(vrel->oid, vrel->relation, options, params,
|
||||
vrel->va_cols, in_outer_xact, vac_strategy);
|
||||
|
||||
if (use_own_xacts)
|
||||
{
|
||||
@ -375,25 +409,33 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a list of Oids for each relation to be processed
|
||||
* Given a VacuumRelation, fill in the table OID if it wasn't specified,
|
||||
* and optionally add VacuumRelations for partitions of the table.
|
||||
*
|
||||
* The list is built in vac_context so that it will survive across our
|
||||
* per-relation transactions.
|
||||
* 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.
|
||||
* Presently, only autovacuum supplies OIDs when calling vacuum(), and
|
||||
* it does not want us to expand partitioned tables.
|
||||
*
|
||||
* We take care not to modify the input data structure, but instead build
|
||||
* new VacuumRelation(s) to return. (But note that they will reference
|
||||
* unmodified parts of the input, eg column lists.) New data structures
|
||||
* are made in vac_context.
|
||||
*/
|
||||
static List *
|
||||
get_rel_oids(Oid relid, const RangeVar *vacrel)
|
||||
expand_vacuum_rel(VacuumRelation *vrel)
|
||||
{
|
||||
List *oid_list = NIL;
|
||||
List *vacrels = NIL;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* OID supplied by VACUUM's caller? */
|
||||
if (OidIsValid(relid))
|
||||
/* If caller supplied OID, there's nothing we need do here. */
|
||||
if (OidIsValid(vrel->oid))
|
||||
{
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
oid_list = lappend_oid(oid_list, relid);
|
||||
vacrels = lappend(vacrels, vrel);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
else if (vacrel)
|
||||
else
|
||||
{
|
||||
/* Process a specific relation, and possibly partitions thereof */
|
||||
Oid relid;
|
||||
@ -406,7 +448,16 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
|
||||
* below, as well as find_all_inheritors's expectation that the caller
|
||||
* holds some lock on the starting relation.
|
||||
*/
|
||||
relid = RangeVarGetRelid(vacrel, AccessShareLock, false);
|
||||
relid = RangeVarGetRelid(vrel->relation, AccessShareLock, false);
|
||||
|
||||
/*
|
||||
* Make a returnable VacuumRelation for this rel.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
vacrels = lappend(vacrels, makeVacuumRelation(vrel->relation,
|
||||
relid,
|
||||
vrel->va_cols));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
/*
|
||||
* To check whether the relation is a partitioned table, fetch its
|
||||
@ -420,19 +471,36 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* Make relation list entries for this rel and its partitions, if any.
|
||||
* Note that the list returned by find_all_inheritors() includes the
|
||||
* passed-in OID at its head. There's no point in taking locks on the
|
||||
* individual partitions yet, and doing so would just add unnecessary
|
||||
* deadlock risk.
|
||||
* 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.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
if (include_parts)
|
||||
oid_list = list_concat(oid_list,
|
||||
find_all_inheritors(relid, NoLock, NULL));
|
||||
else
|
||||
oid_list = lappend_oid(oid_list, relid);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
{
|
||||
List *part_oids = find_all_inheritors(relid, NoLock, NULL);
|
||||
ListCell *part_lc;
|
||||
|
||||
foreach(part_lc, part_oids)
|
||||
{
|
||||
Oid part_oid = lfirst_oid(part_lc);
|
||||
|
||||
if (part_oid == relid)
|
||||
continue; /* ignore original table */
|
||||
|
||||
/*
|
||||
* We omit a RangeVar since it wouldn't be appropriate to
|
||||
* complain about failure to open one of these relations
|
||||
* later.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
|
||||
part_oid,
|
||||
vrel->va_cols));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Release lock again. This means that by the time we actually try to
|
||||
@ -447,45 +515,57 @@ get_rel_oids(Oid relid, const RangeVar *vacrel)
|
||||
*/
|
||||
UnlockRelationOid(relid, AccessShareLock);
|
||||
}
|
||||
else
|
||||
|
||||
return vacrels;
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a list of VacuumRelations for all vacuumable rels in
|
||||
* the current database. The list is built in vac_context.
|
||||
*/
|
||||
static List *
|
||||
get_all_vacuum_rels(void)
|
||||
{
|
||||
List *vacrels = NIL;
|
||||
Relation pgclass;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
|
||||
pgclass = heap_open(RelationRelationId, AccessShareLock);
|
||||
|
||||
scan = heap_beginscan_catalog(pgclass, 0, NULL);
|
||||
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/*
|
||||
* Process all plain relations and materialized views listed in
|
||||
* pg_class
|
||||
* We include partitioned tables here; depending on which operation is
|
||||
* to be performed, caller will decide whether to process or ignore
|
||||
* them.
|
||||
*/
|
||||
Relation pgclass;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
if (classForm->relkind != RELKIND_RELATION &&
|
||||
classForm->relkind != RELKIND_MATVIEW &&
|
||||
classForm->relkind != RELKIND_PARTITIONED_TABLE)
|
||||
continue;
|
||||
|
||||
pgclass = heap_open(RelationRelationId, AccessShareLock);
|
||||
|
||||
scan = heap_beginscan_catalog(pgclass, 0, NULL);
|
||||
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
/*
|
||||
* We include partitioned tables here; depending on which
|
||||
* operation is to be performed, caller will decide whether to
|
||||
* process or ignore them.
|
||||
*/
|
||||
if (classForm->relkind != RELKIND_RELATION &&
|
||||
classForm->relkind != RELKIND_MATVIEW &&
|
||||
classForm->relkind != RELKIND_PARTITIONED_TABLE)
|
||||
continue;
|
||||
|
||||
/* Make a relation list entry for this rel */
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
oid_list = lappend_oid(oid_list, HeapTupleGetOid(tuple));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(pgclass, AccessShareLock);
|
||||
/*
|
||||
* Build VacuumRelation(s) specifying the table OIDs to be processed.
|
||||
* We omit a RangeVar since it wouldn't be appropriate to complain
|
||||
* about failure to open one of these relations later.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(vac_context);
|
||||
vacrels = lappend(vacrels, makeVacuumRelation(NULL,
|
||||
HeapTupleGetOid(tuple),
|
||||
NIL));
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
return oid_list;
|
||||
heap_endscan(scan);
|
||||
heap_close(pgclass, AccessShareLock);
|
||||
|
||||
return vacrels;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3766,7 +3766,18 @@ _copyVacuumStmt(const VacuumStmt *from)
|
||||
VacuumStmt *newnode = makeNode(VacuumStmt);
|
||||
|
||||
COPY_SCALAR_FIELD(options);
|
||||
COPY_NODE_FIELD(rels);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static VacuumRelation *
|
||||
_copyVacuumRelation(const VacuumRelation *from)
|
||||
{
|
||||
VacuumRelation *newnode = makeNode(VacuumRelation);
|
||||
|
||||
COPY_NODE_FIELD(relation);
|
||||
COPY_SCALAR_FIELD(oid);
|
||||
COPY_NODE_FIELD(va_cols);
|
||||
|
||||
return newnode;
|
||||
@ -5215,6 +5226,9 @@ copyObjectImpl(const void *from)
|
||||
case T_VacuumStmt:
|
||||
retval = _copyVacuumStmt(from);
|
||||
break;
|
||||
case T_VacuumRelation:
|
||||
retval = _copyVacuumRelation(from);
|
||||
break;
|
||||
case T_ExplainStmt:
|
||||
retval = _copyExplainStmt(from);
|
||||
break;
|
||||
|
@ -1663,7 +1663,16 @@ static bool
|
||||
_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
|
||||
{
|
||||
COMPARE_SCALAR_FIELD(options);
|
||||
COMPARE_NODE_FIELD(rels);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalVacuumRelation(const VacuumRelation *a, const VacuumRelation *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(relation);
|
||||
COMPARE_SCALAR_FIELD(oid);
|
||||
COMPARE_NODE_FIELD(va_cols);
|
||||
|
||||
return true;
|
||||
@ -3361,6 +3370,9 @@ equal(const void *a, const void *b)
|
||||
case T_VacuumStmt:
|
||||
retval = _equalVacuumStmt(a, b);
|
||||
break;
|
||||
case T_VacuumRelation:
|
||||
retval = _equalVacuumRelation(a, b);
|
||||
break;
|
||||
case T_ExplainStmt:
|
||||
retval = _equalExplainStmt(a, b);
|
||||
break;
|
||||
|
@ -611,3 +611,18 @@ makeGroupingSet(GroupingSetKind kind, List *content, int location)
|
||||
n->location = location;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* makeVacuumRelation -
|
||||
* create a VacuumRelation node
|
||||
*/
|
||||
VacuumRelation *
|
||||
makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols)
|
||||
{
|
||||
VacuumRelation *v = makeNode(VacuumRelation);
|
||||
|
||||
v->relation = relation;
|
||||
v->oid = oid;
|
||||
v->va_cols = va_cols;
|
||||
return v;
|
||||
}
|
||||
|
@ -365,6 +365,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
%type <list> DefACLOptionList
|
||||
%type <ival> import_qualification_type
|
||||
%type <importqual> import_qualification
|
||||
%type <node> vacuum_relation
|
||||
|
||||
%type <list> stmtblock stmtmulti
|
||||
OptTableElementList TableElementList OptInherit definition
|
||||
@ -396,6 +397,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
||||
transform_element_list transform_type_list
|
||||
TriggerTransitions TriggerReferencing
|
||||
publication_name_list
|
||||
vacuum_relation_list opt_vacuum_relation_list
|
||||
|
||||
%type <list> group_by_list
|
||||
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
|
||||
@ -10147,7 +10149,7 @@ cluster_index_specification:
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
||||
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_vacuum_relation_list
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_VACUUM;
|
||||
@ -10157,22 +10159,7 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
||||
n->options |= VACOPT_FREEZE;
|
||||
if ($4)
|
||||
n->options |= VACOPT_VERBOSE;
|
||||
n->relation = NULL;
|
||||
n->va_cols = NIL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| VACUUM opt_full opt_freeze opt_verbose qualified_name
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_VACUUM;
|
||||
if ($2)
|
||||
n->options |= VACOPT_FULL;
|
||||
if ($3)
|
||||
n->options |= VACOPT_FREEZE;
|
||||
if ($4)
|
||||
n->options |= VACOPT_VERBOSE;
|
||||
n->relation = $5;
|
||||
n->va_cols = NIL;
|
||||
n->rels = $5;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| VACUUM opt_full opt_freeze opt_verbose AnalyzeStmt
|
||||
@ -10187,22 +10174,11 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
|
||||
n->options |= VACOPT_VERBOSE;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| VACUUM '(' vacuum_option_list ')'
|
||||
| VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_list
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_VACUUM | $3;
|
||||
n->relation = NULL;
|
||||
n->va_cols = NIL;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| VACUUM '(' vacuum_option_list ')' qualified_name opt_name_list
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_VACUUM | $3;
|
||||
n->relation = $5;
|
||||
n->va_cols = $6;
|
||||
if (n->va_cols != NIL) /* implies analyze */
|
||||
n->options |= VACOPT_ANALYZE;
|
||||
n->rels = $5;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
@ -10229,25 +10205,13 @@ vacuum_option_elem:
|
||||
}
|
||||
;
|
||||
|
||||
AnalyzeStmt:
|
||||
analyze_keyword opt_verbose
|
||||
AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_ANALYZE;
|
||||
if ($2)
|
||||
n->options |= VACOPT_VERBOSE;
|
||||
n->relation = NULL;
|
||||
n->va_cols = NIL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| analyze_keyword opt_verbose qualified_name opt_name_list
|
||||
{
|
||||
VacuumStmt *n = makeNode(VacuumStmt);
|
||||
n->options = VACOPT_ANALYZE;
|
||||
if ($2)
|
||||
n->options |= VACOPT_VERBOSE;
|
||||
n->relation = $3;
|
||||
n->va_cols = $4;
|
||||
n->rels = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@ -10275,6 +10239,25 @@ opt_name_list:
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
vacuum_relation:
|
||||
qualified_name opt_name_list
|
||||
{
|
||||
$$ = (Node *) makeVacuumRelation($1, InvalidOid, $2);
|
||||
}
|
||||
;
|
||||
|
||||
vacuum_relation_list:
|
||||
vacuum_relation
|
||||
{ $$ = list_make1($1); }
|
||||
| vacuum_relation_list ',' vacuum_relation
|
||||
{ $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
opt_vacuum_relation_list:
|
||||
vacuum_relation_list { $$ = $1; }
|
||||
| /*EMPTY*/ { $$ = NIL; }
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
@ -79,6 +79,7 @@
|
||||
#include "lib/ilist.h"
|
||||
#include "libpq/pqsignal.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "postmaster/fork_process.h"
|
||||
@ -3081,20 +3082,19 @@ relation_needs_vacanalyze(Oid relid,
|
||||
static void
|
||||
autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
|
||||
{
|
||||
RangeVar rangevar;
|
||||
|
||||
/* Set up command parameters --- use local variables instead of palloc */
|
||||
MemSet(&rangevar, 0, sizeof(rangevar));
|
||||
|
||||
rangevar.schemaname = tab->at_nspname;
|
||||
rangevar.relname = tab->at_relname;
|
||||
rangevar.location = -1;
|
||||
RangeVar *rangevar;
|
||||
VacuumRelation *rel;
|
||||
List *rel_list;
|
||||
|
||||
/* Let pgstat know what we're doing */
|
||||
autovac_report_activity(tab);
|
||||
|
||||
vacuum(tab->at_vacoptions, &rangevar, tab->at_relid, &tab->at_params, NIL,
|
||||
bstrategy, true);
|
||||
/* Set up one VacuumRelation target, identified by OID, for vacuum() */
|
||||
rangevar = makeRangeVar(tab->at_nspname, tab->at_relname, -1);
|
||||
rel = makeVacuumRelation(rangevar, tab->at_relid, NIL);
|
||||
rel_list = list_make1(rel);
|
||||
|
||||
vacuum(tab->at_vacoptions, rel_list, &tab->at_params, bstrategy, true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -157,8 +157,7 @@ extern int vacuum_multixact_freeze_table_age;
|
||||
|
||||
/* in commands/vacuum.c */
|
||||
extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel);
|
||||
extern void vacuum(int options, RangeVar *relation, Oid relid,
|
||||
VacuumParams *params, List *va_cols,
|
||||
extern void vacuum(int options, List *relations, VacuumParams *params,
|
||||
BufferAccessStrategy bstrategy, bool isTopLevel);
|
||||
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
|
||||
int *nindexes, Relation **Irel);
|
||||
|
@ -86,4 +86,6 @@ extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
|
||||
|
||||
extern GroupingSet *makeGroupingSet(GroupingSetKind kind, List *content, int location);
|
||||
|
||||
extern VacuumRelation *makeVacuumRelation(RangeVar *relation, Oid oid, List *va_cols);
|
||||
|
||||
#endif /* MAKEFUNC_H */
|
||||
|
@ -468,6 +468,7 @@ typedef enum NodeTag
|
||||
T_PartitionBoundSpec,
|
||||
T_PartitionRangeDatum,
|
||||
T_PartitionCmd,
|
||||
T_VacuumRelation,
|
||||
|
||||
/*
|
||||
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
|
||||
|
@ -1778,8 +1778,8 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
|
||||
AlterTableType subtype; /* Type of table alteration to apply */
|
||||
char *name; /* column, constraint, or trigger to act on,
|
||||
* or tablespace */
|
||||
int16 num; /* attribute number for columns referenced
|
||||
* by number */
|
||||
int16 num; /* attribute number for columns referenced by
|
||||
* number */
|
||||
RoleSpec *newowner;
|
||||
Node *def; /* definition of new column, index,
|
||||
* constraint, or parent table */
|
||||
@ -3098,12 +3098,26 @@ typedef enum VacuumOption
|
||||
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
|
||||
} VacuumOption;
|
||||
|
||||
/*
|
||||
* Info about a single target table of VACUUM/ANALYZE.
|
||||
*
|
||||
* If the OID field is set, it always identifies the table to process.
|
||||
* Then the relation field can be NULL; if it isn't, it's used only to report
|
||||
* failure to open/lock the relation.
|
||||
*/
|
||||
typedef struct VacuumRelation
|
||||
{
|
||||
NodeTag type;
|
||||
RangeVar *relation; /* table name to process, or NULL */
|
||||
Oid oid; /* table's OID; InvalidOid if not looked up */
|
||||
List *va_cols; /* list of column names, or NIL for all */
|
||||
} VacuumRelation;
|
||||
|
||||
typedef struct VacuumStmt
|
||||
{
|
||||
NodeTag type;
|
||||
int options; /* OR of VacuumOption flags */
|
||||
RangeVar *relation; /* single table to process, or NULL */
|
||||
List *va_cols; /* list of column names, or NIL for all */
|
||||
List *rels; /* list of VacuumRelation, or NIL for all */
|
||||
} VacuumStmt;
|
||||
|
||||
/* ----------------------
|
||||
|
@ -80,8 +80,6 @@ CONTEXT: SQL function "do_analyze" statement 1
|
||||
SQL function "wrap_do_analyze" statement 1
|
||||
VACUUM FULL vactst;
|
||||
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
|
||||
DROP TABLE vaccluster;
|
||||
DROP TABLE vactst;
|
||||
-- partitioned table
|
||||
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
|
||||
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
|
||||
@ -95,4 +93,25 @@ VACUUM ANALYZE vacparted(a,b,a);
|
||||
ERROR: column "a" of relation "vacparted" appears more than once
|
||||
ANALYZE vacparted(a,b,b);
|
||||
ERROR: column "b" of relation "vacparted" appears more than once
|
||||
-- multiple tables specified
|
||||
VACUUM vaccluster, vactst;
|
||||
VACUUM vacparted, does_not_exist;
|
||||
ERROR: relation "does_not_exist" does not exist
|
||||
VACUUM (FREEZE) vacparted, vaccluster, vactst;
|
||||
VACUUM (FREEZE) does_not_exist, vaccluster;
|
||||
ERROR: relation "does_not_exist" does not exist
|
||||
VACUUM ANALYZE vactst, vacparted (a);
|
||||
VACUUM ANALYZE vactst (does_not_exist), vacparted (b);
|
||||
ERROR: column "does_not_exist" of relation "vactst" does not exist
|
||||
VACUUM FULL vacparted, vactst;
|
||||
VACUUM FULL vactst, vacparted (a, b), vaccluster (i);
|
||||
ERROR: ANALYZE option must be specified when a column list is provided
|
||||
ANALYZE vactst, vacparted;
|
||||
ANALYZE vacparted (b), vactst;
|
||||
ANALYZE vactst, does_not_exist, vacparted;
|
||||
ERROR: relation "does_not_exist" does not exist
|
||||
ANALYZE vactst (i), vacparted (does_not_exist);
|
||||
ERROR: column "does_not_exist" of relation "vacparted" does not exist
|
||||
DROP TABLE vaccluster;
|
||||
DROP TABLE vactst;
|
||||
DROP TABLE vacparted;
|
||||
|
@ -62,9 +62,6 @@ VACUUM FULL vactst;
|
||||
|
||||
VACUUM (DISABLE_PAGE_SKIPPING) vaccluster;
|
||||
|
||||
DROP TABLE vaccluster;
|
||||
DROP TABLE vactst;
|
||||
|
||||
-- partitioned table
|
||||
CREATE TABLE vacparted (a int, b char) PARTITION BY LIST (a);
|
||||
CREATE TABLE vacparted1 PARTITION OF vacparted FOR VALUES IN (1);
|
||||
@ -78,4 +75,20 @@ VACUUM (FREEZE) vacparted;
|
||||
VACUUM ANALYZE vacparted(a,b,a);
|
||||
ANALYZE vacparted(a,b,b);
|
||||
|
||||
-- multiple tables specified
|
||||
VACUUM vaccluster, vactst;
|
||||
VACUUM vacparted, does_not_exist;
|
||||
VACUUM (FREEZE) vacparted, vaccluster, vactst;
|
||||
VACUUM (FREEZE) does_not_exist, vaccluster;
|
||||
VACUUM ANALYZE vactst, vacparted (a);
|
||||
VACUUM ANALYZE vactst (does_not_exist), vacparted (b);
|
||||
VACUUM FULL vacparted, vactst;
|
||||
VACUUM FULL vactst, vacparted (a, b), vaccluster (i);
|
||||
ANALYZE vactst, vacparted;
|
||||
ANALYZE vacparted (b), vactst;
|
||||
ANALYZE vactst, does_not_exist, vacparted;
|
||||
ANALYZE vactst (i), vacparted (does_not_exist);
|
||||
|
||||
DROP TABLE vaccluster;
|
||||
DROP TABLE vactst;
|
||||
DROP TABLE vacparted;
|
||||
|
Loading…
x
Reference in New Issue
Block a user