mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Revise parse tree representation for VACUUM and ANALYZE.
Like commit f41551f61f
, this aims
to make it easier to add non-Boolean options to VACUUM (or, in
this case, to ANALYZE). Instead of building up a bitmap of
options directly in the parser, build up a list of DefElem
objects and let ExecVacuum() sort it out; right now, we make
no use of the fact that a DefElem can carry an associated value,
but it will be easy to make that change in the future.
Masahiko Sawada
Discussion: http://postgr.es/m/CAD21AoATE4sn0jFFH3NcfUZXkU2BMbjBWB_kDj-XWYA-LXDcQA@mail.gmail.com
This commit is contained in:
@ -83,20 +83,55 @@ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
|
|||||||
* happen in vacuum().
|
* happen in vacuum().
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
|
ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
|
||||||
{
|
{
|
||||||
VacuumParams params;
|
VacuumParams params;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
params.options = vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE;
|
||||||
|
|
||||||
|
/* Parse options list */
|
||||||
|
foreach(lc, vacstmt->options)
|
||||||
|
{
|
||||||
|
DefElem *opt = (DefElem *) lfirst(lc);
|
||||||
|
|
||||||
|
/* Parse common options for VACUUM and ANALYZE */
|
||||||
|
if (strcmp(opt->defname, "verbose") == 0)
|
||||||
|
params.options |= VACOPT_VERBOSE;
|
||||||
|
else if (strcmp(opt->defname, "skip_locked") == 0)
|
||||||
|
params.options |= VACOPT_SKIP_LOCKED;
|
||||||
|
else if (!vacstmt->is_vacuumcmd)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("unrecognized ANALYZE option \"%s\"", opt->defname),
|
||||||
|
parser_errposition(pstate, opt->location)));
|
||||||
|
|
||||||
|
/* Parse options available on VACUUM */
|
||||||
|
else if (strcmp(opt->defname, "analyze") == 0)
|
||||||
|
params.options |= VACOPT_ANALYZE;
|
||||||
|
else if (strcmp(opt->defname, "freeze") == 0)
|
||||||
|
params.options |= VACOPT_FREEZE;
|
||||||
|
else if (strcmp(opt->defname, "full") == 0)
|
||||||
|
params.options |= VACOPT_FULL;
|
||||||
|
else if (strcmp(opt->defname, "disable_page_skipping") == 0)
|
||||||
|
params.options |= VACOPT_DISABLE_PAGE_SKIPPING;
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("unrecognized VACUUM option \"%s\"", opt->defname),
|
||||||
|
parser_errposition(pstate, opt->location)));
|
||||||
|
}
|
||||||
|
|
||||||
/* sanity checks on options */
|
/* sanity checks on options */
|
||||||
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
|
Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
|
||||||
Assert((vacstmt->options & VACOPT_VACUUM) ||
|
Assert((params.options & VACOPT_VACUUM) ||
|
||||||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
|
!(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
|
||||||
Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
|
Assert(!(params.options & VACOPT_SKIPTOAST));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
|
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
|
||||||
*/
|
*/
|
||||||
if (!(vacstmt->options & VACOPT_ANALYZE))
|
if (!(params.options & VACOPT_ANALYZE))
|
||||||
{
|
{
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
|
|
||||||
@ -111,14 +146,11 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy options from parse tree */
|
|
||||||
params.options = vacstmt->options;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All freeze ages are zero if the FREEZE option is given; otherwise pass
|
* All freeze ages are zero if the FREEZE option is given; otherwise pass
|
||||||
* them as -1 which means to use the default values.
|
* them as -1 which means to use the default values.
|
||||||
*/
|
*/
|
||||||
if (vacstmt->options & VACOPT_FREEZE)
|
if (params.options & VACOPT_FREEZE)
|
||||||
{
|
{
|
||||||
params.freeze_min_age = 0;
|
params.freeze_min_age = 0;
|
||||||
params.freeze_table_age = 0;
|
params.freeze_table_age = 0;
|
||||||
|
@ -306,8 +306,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
create_extension_opt_item alter_extension_opt_item
|
create_extension_opt_item alter_extension_opt_item
|
||||||
|
|
||||||
%type <ival> opt_lock lock_type cast_context
|
%type <ival> opt_lock lock_type cast_context
|
||||||
%type <ival> vacuum_option_list vacuum_option_elem
|
%type <str> vac_analyze_option_name
|
||||||
analyze_option_list analyze_option_elem
|
%type <defelt> vac_analyze_option_elem
|
||||||
|
%type <list> vac_analyze_option_list
|
||||||
%type <boolean> opt_or_replace
|
%type <boolean> opt_or_replace
|
||||||
opt_grant_grant_option opt_grant_admin_option
|
opt_grant_grant_option opt_grant_admin_option
|
||||||
opt_nowait opt_if_exists opt_with_data
|
opt_nowait opt_if_exists opt_with_data
|
||||||
@ -10460,85 +10461,62 @@ cluster_index_specification:
|
|||||||
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relation_list
|
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relation_list
|
||||||
{
|
{
|
||||||
VacuumStmt *n = makeNode(VacuumStmt);
|
VacuumStmt *n = makeNode(VacuumStmt);
|
||||||
n->options = VACOPT_VACUUM;
|
n->options = NIL;
|
||||||
if ($2)
|
if ($2)
|
||||||
n->options |= VACOPT_FULL;
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("full", NULL, @2));
|
||||||
if ($3)
|
if ($3)
|
||||||
n->options |= VACOPT_FREEZE;
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("freeze", NULL, @3));
|
||||||
if ($4)
|
if ($4)
|
||||||
n->options |= VACOPT_VERBOSE;
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("verbose", NULL, @4));
|
||||||
if ($5)
|
if ($5)
|
||||||
n->options |= VACOPT_ANALYZE;
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("analyze", NULL, @5));
|
||||||
n->rels = $6;
|
n->rels = $6;
|
||||||
|
n->is_vacuumcmd = true;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_list
|
| VACUUM '(' vac_analyze_option_list ')' opt_vacuum_relation_list
|
||||||
{
|
{
|
||||||
VacuumStmt *n = makeNode(VacuumStmt);
|
VacuumStmt *n = makeNode(VacuumStmt);
|
||||||
n->options = VACOPT_VACUUM | $3;
|
n->options = $3;
|
||||||
n->rels = $5;
|
n->rels = $5;
|
||||||
|
n->is_vacuumcmd = true;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
vacuum_option_list:
|
|
||||||
vacuum_option_elem { $$ = $1; }
|
|
||||||
| vacuum_option_list ',' vacuum_option_elem { $$ = $1 | $3; }
|
|
||||||
;
|
|
||||||
|
|
||||||
vacuum_option_elem:
|
|
||||||
analyze_keyword { $$ = VACOPT_ANALYZE; }
|
|
||||||
| VERBOSE { $$ = VACOPT_VERBOSE; }
|
|
||||||
| FREEZE { $$ = VACOPT_FREEZE; }
|
|
||||||
| FULL { $$ = VACOPT_FULL; }
|
|
||||||
| IDENT
|
|
||||||
{
|
|
||||||
if (strcmp($1, "disable_page_skipping") == 0)
|
|
||||||
$$ = VACOPT_DISABLE_PAGE_SKIPPING;
|
|
||||||
else if (strcmp($1, "skip_locked") == 0)
|
|
||||||
$$ = VACOPT_SKIP_LOCKED;
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("unrecognized VACUUM option \"%s\"", $1),
|
|
||||||
parser_errposition(@1)));
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
|
AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
|
||||||
{
|
{
|
||||||
VacuumStmt *n = makeNode(VacuumStmt);
|
VacuumStmt *n = makeNode(VacuumStmt);
|
||||||
n->options = VACOPT_ANALYZE;
|
n->options = NIL;
|
||||||
if ($2)
|
if ($2)
|
||||||
n->options |= VACOPT_VERBOSE;
|
n->options = lappend(n->options,
|
||||||
|
makeDefElem("verbose", NULL, @2));
|
||||||
n->rels = $3;
|
n->rels = $3;
|
||||||
|
n->is_vacuumcmd = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| analyze_keyword '(' analyze_option_list ')' opt_vacuum_relation_list
|
| analyze_keyword '(' vac_analyze_option_list ')' opt_vacuum_relation_list
|
||||||
{
|
{
|
||||||
VacuumStmt *n = makeNode(VacuumStmt);
|
VacuumStmt *n = makeNode(VacuumStmt);
|
||||||
n->options = VACOPT_ANALYZE | $3;
|
n->options = $3;
|
||||||
n->rels = $5;
|
n->rels = $5;
|
||||||
|
n->is_vacuumcmd = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
analyze_option_list:
|
vac_analyze_option_list:
|
||||||
analyze_option_elem { $$ = $1; }
|
vac_analyze_option_elem
|
||||||
| analyze_option_list ',' analyze_option_elem { $$ = $1 | $3; }
|
|
||||||
;
|
|
||||||
|
|
||||||
analyze_option_elem:
|
|
||||||
VERBOSE { $$ = VACOPT_VERBOSE; }
|
|
||||||
| IDENT
|
|
||||||
{
|
{
|
||||||
if (strcmp($1, "skip_locked") == 0)
|
$$ = list_make1($1);
|
||||||
$$ = VACOPT_SKIP_LOCKED;
|
}
|
||||||
else
|
| vac_analyze_option_list ',' vac_analyze_option_elem
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
$$ = lappend($1, $3);
|
||||||
errmsg("unrecognized ANALYZE option \"%s\"", $1),
|
|
||||||
parser_errposition(@1)));
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
@ -10547,6 +10525,18 @@ analyze_keyword:
|
|||||||
| ANALYSE /* British */ {}
|
| ANALYSE /* British */ {}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
vac_analyze_option_elem:
|
||||||
|
vac_analyze_option_name
|
||||||
|
{
|
||||||
|
$$ = makeDefElem($1, NULL, @1);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
vac_analyze_option_name:
|
||||||
|
NonReservedWord { $$ = $1; }
|
||||||
|
| analyze_keyword { $$ = "analyze"; }
|
||||||
|
;
|
||||||
|
|
||||||
opt_analyze:
|
opt_analyze:
|
||||||
analyze_keyword { $$ = true; }
|
analyze_keyword { $$ = true; }
|
||||||
| /*EMPTY*/ { $$ = false; }
|
| /*EMPTY*/ { $$ = false; }
|
||||||
|
@ -664,10 +664,10 @@ standard_ProcessUtility(PlannedStmt *pstmt,
|
|||||||
VacuumStmt *stmt = (VacuumStmt *) parsetree;
|
VacuumStmt *stmt = (VacuumStmt *) parsetree;
|
||||||
|
|
||||||
/* we choose to allow this during "read only" transactions */
|
/* we choose to allow this during "read only" transactions */
|
||||||
PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ?
|
PreventCommandDuringRecovery(stmt->is_vacuumcmd ?
|
||||||
"VACUUM" : "ANALYZE");
|
"VACUUM" : "ANALYZE");
|
||||||
/* forbidden in parallel mode due to CommandIsReadOnly */
|
/* forbidden in parallel mode due to CommandIsReadOnly */
|
||||||
ExecVacuum(stmt, isTopLevel);
|
ExecVacuum(pstate, stmt, isTopLevel);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2570,7 +2570,7 @@ CreateCommandTag(Node *parsetree)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_VacuumStmt:
|
case T_VacuumStmt:
|
||||||
if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
|
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
|
||||||
tag = "VACUUM";
|
tag = "VACUUM";
|
||||||
else
|
else
|
||||||
tag = "ANALYZE";
|
tag = "ANALYZE";
|
||||||
|
@ -136,8 +136,23 @@ typedef struct VacAttrStats
|
|||||||
int rowstride;
|
int rowstride;
|
||||||
} VacAttrStats;
|
} VacAttrStats;
|
||||||
|
|
||||||
|
typedef enum VacuumOption
|
||||||
|
{
|
||||||
|
VACOPT_VACUUM = 1 << 0, /* do VACUUM */
|
||||||
|
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
|
||||||
|
VACOPT_VERBOSE = 1 << 2, /* print progress info */
|
||||||
|
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
|
||||||
|
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
|
||||||
|
VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */
|
||||||
|
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
|
||||||
|
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
|
||||||
|
} VacuumOption;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parameters customizing behavior of VACUUM and ANALYZE.
|
* Parameters customizing behavior of VACUUM and ANALYZE.
|
||||||
|
*
|
||||||
|
* Note that at least one of VACOPT_VACUUM and VACOPT_ANALYZE must be set
|
||||||
|
* in options.
|
||||||
*/
|
*/
|
||||||
typedef struct VacuumParams
|
typedef struct VacuumParams
|
||||||
{
|
{
|
||||||
@ -163,7 +178,7 @@ extern int vacuum_multixact_freeze_table_age;
|
|||||||
|
|
||||||
|
|
||||||
/* in commands/vacuum.c */
|
/* in commands/vacuum.c */
|
||||||
extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel);
|
extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
|
||||||
extern void vacuum(List *relations, VacuumParams *params,
|
extern void vacuum(List *relations, VacuumParams *params,
|
||||||
BufferAccessStrategy bstrategy, bool isTopLevel);
|
BufferAccessStrategy bstrategy, bool isTopLevel);
|
||||||
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
|
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
|
||||||
|
@ -3151,21 +3151,16 @@ typedef struct ClusterStmt
|
|||||||
* Vacuum and Analyze Statements
|
* Vacuum and Analyze Statements
|
||||||
*
|
*
|
||||||
* Even though these are nominally two statements, it's convenient to use
|
* Even though these are nominally two statements, it's convenient to use
|
||||||
* just one node type for both. Note that at least one of VACOPT_VACUUM
|
* just one node type for both.
|
||||||
* and VACOPT_ANALYZE must be set in options.
|
|
||||||
* ----------------------
|
* ----------------------
|
||||||
*/
|
*/
|
||||||
typedef enum VacuumOption
|
typedef struct VacuumStmt
|
||||||
{
|
{
|
||||||
VACOPT_VACUUM = 1 << 0, /* do VACUUM */
|
NodeTag type;
|
||||||
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
|
List *options; /* list of DefElem nodes */
|
||||||
VACOPT_VERBOSE = 1 << 2, /* print progress info */
|
List *rels; /* list of VacuumRelation, or NIL for all */
|
||||||
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
|
bool is_vacuumcmd; /* true for VACUUM, false for ANALYZE */
|
||||||
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
|
} VacuumStmt;
|
||||||
VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */
|
|
||||||
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
|
|
||||||
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
|
|
||||||
} VacuumOption;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Info about a single target table of VACUUM/ANALYZE.
|
* Info about a single target table of VACUUM/ANALYZE.
|
||||||
@ -3182,13 +3177,6 @@ typedef struct VacuumRelation
|
|||||||
List *va_cols; /* list of column names, or NIL for all */
|
List *va_cols; /* list of column names, or NIL for all */
|
||||||
} VacuumRelation;
|
} VacuumRelation;
|
||||||
|
|
||||||
typedef struct VacuumStmt
|
|
||||||
{
|
|
||||||
NodeTag type;
|
|
||||||
int options; /* OR of VacuumOption flags */
|
|
||||||
List *rels; /* list of VacuumRelation, or NIL for all */
|
|
||||||
} VacuumStmt;
|
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Explain Statement
|
* Explain Statement
|
||||||
*
|
*
|
||||||
|
@ -116,9 +116,13 @@ ERROR: column "does_not_exist" of relation "vacparted" does not exist
|
|||||||
ANALYZE (VERBOSE) does_not_exist;
|
ANALYZE (VERBOSE) does_not_exist;
|
||||||
ERROR: relation "does_not_exist" does not exist
|
ERROR: relation "does_not_exist" does not exist
|
||||||
ANALYZE (nonexistent-arg) does_not_exist;
|
ANALYZE (nonexistent-arg) does_not_exist;
|
||||||
ERROR: unrecognized ANALYZE option "nonexistent"
|
ERROR: syntax error at or near "-"
|
||||||
LINE 1: ANALYZE (nonexistent-arg) does_not_exist;
|
LINE 1: ANALYZE (nonexistent-arg) does_not_exist;
|
||||||
^
|
^
|
||||||
|
ANALYZE (nonexistentarg) does_not_exit;
|
||||||
|
ERROR: unrecognized ANALYZE option "nonexistentarg"
|
||||||
|
LINE 1: ANALYZE (nonexistentarg) does_not_exit;
|
||||||
|
^
|
||||||
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
|
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
|
||||||
-- relation still errors out.
|
-- relation still errors out.
|
||||||
ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist;
|
ANALYZE (SKIP_LOCKED, VERBOSE) does_not_exist;
|
||||||
|
@ -92,6 +92,7 @@ ANALYZE vactst (i), vacparted (does_not_exist);
|
|||||||
-- parenthesized syntax for ANALYZE
|
-- parenthesized syntax for ANALYZE
|
||||||
ANALYZE (VERBOSE) does_not_exist;
|
ANALYZE (VERBOSE) does_not_exist;
|
||||||
ANALYZE (nonexistent-arg) does_not_exist;
|
ANALYZE (nonexistent-arg) does_not_exist;
|
||||||
|
ANALYZE (nonexistentarg) does_not_exit;
|
||||||
|
|
||||||
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
|
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
|
||||||
-- relation still errors out.
|
-- relation still errors out.
|
||||||
|
Reference in New Issue
Block a user