Skip to content

Commit 6776142

Browse files
committed
Revise parse tree representation for VACUUM and ANALYZE.
Like commit f41551f, 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
1 parent f41551f commit 6776142

File tree

7 files changed

+116
-86
lines changed

7 files changed

+116
-86
lines changed

src/backend/commands/vacuum.c

+42-10
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,55 @@ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
8383
* happen in vacuum().
8484
*/
8585
void
86-
ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
86+
ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
8787
{
8888
VacuumParams params;
89+
ListCell *lc;
90+
91+
params.options = vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE;
92+
93+
/* Parse options list */
94+
foreach(lc, vacstmt->options)
95+
{
96+
DefElem *opt = (DefElem *) lfirst(lc);
97+
98+
/* Parse common options for VACUUM and ANALYZE */
99+
if (strcmp(opt->defname, "verbose") == 0)
100+
params.options |= VACOPT_VERBOSE;
101+
else if (strcmp(opt->defname, "skip_locked") == 0)
102+
params.options |= VACOPT_SKIP_LOCKED;
103+
else if (!vacstmt->is_vacuumcmd)
104+
ereport(ERROR,
105+
(errcode(ERRCODE_SYNTAX_ERROR),
106+
errmsg("unrecognized ANALYZE option \"%s\"", opt->defname),
107+
parser_errposition(pstate, opt->location)));
108+
109+
/* Parse options available on VACUUM */
110+
else if (strcmp(opt->defname, "analyze") == 0)
111+
params.options |= VACOPT_ANALYZE;
112+
else if (strcmp(opt->defname, "freeze") == 0)
113+
params.options |= VACOPT_FREEZE;
114+
else if (strcmp(opt->defname, "full") == 0)
115+
params.options |= VACOPT_FULL;
116+
else if (strcmp(opt->defname, "disable_page_skipping") == 0)
117+
params.options |= VACOPT_DISABLE_PAGE_SKIPPING;
118+
else
119+
ereport(ERROR,
120+
(errcode(ERRCODE_SYNTAX_ERROR),
121+
errmsg("unrecognized VACUUM option \"%s\"", opt->defname),
122+
parser_errposition(pstate, opt->location)));
123+
}
89124

90125
/* sanity checks on options */
91-
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
92-
Assert((vacstmt->options & VACOPT_VACUUM) ||
93-
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
94-
Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
126+
Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
127+
Assert((params.options & VACOPT_VACUUM) ||
128+
!(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
129+
Assert(!(params.options & VACOPT_SKIPTOAST));
95130

96131
/*
97132
* Make sure VACOPT_ANALYZE is specified if any column lists are present.
98133
*/
99-
if (!(vacstmt->options & VACOPT_ANALYZE))
134+
if (!(params.options & VACOPT_ANALYZE))
100135
{
101136
ListCell *lc;
102137

@@ -111,14 +146,11 @@ ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
111146
}
112147
}
113148

114-
/* copy options from parse tree */
115-
params.options = vacstmt->options;
116-
117149
/*
118150
* All freeze ages are zero if the FREEZE option is given; otherwise pass
119151
* them as -1 which means to use the default values.
120152
*/
121-
if (vacstmt->options & VACOPT_FREEZE)
153+
if (params.options & VACOPT_FREEZE)
122154
{
123155
params.freeze_min_age = 0;
124156
params.freeze_table_age = 0;

src/backend/parser/gram.y

+42-52
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
306306
create_extension_opt_item alter_extension_opt_item
307307

308308
%type <ival> opt_lock lock_type cast_context
309-
%type <ival> vacuum_option_list vacuum_option_elem
310-
analyze_option_list analyze_option_elem
309+
%type <str> vac_analyze_option_name
310+
%type <defelt> vac_analyze_option_elem
311+
%type <list> vac_analyze_option_list
311312
%type <boolean> opt_or_replace
312313
opt_grant_grant_option opt_grant_admin_option
313314
opt_nowait opt_if_exists opt_with_data
@@ -10460,85 +10461,62 @@ cluster_index_specification:
1046010461
VacuumStmt: VACUUM opt_full opt_freeze opt_verbose opt_analyze opt_vacuum_relation_list
1046110462
{
1046210463
VacuumStmt *n = makeNode(VacuumStmt);
10463-
n->options = VACOPT_VACUUM;
10464+
n->options = NIL;
1046410465
if ($2)
10465-
n->options |= VACOPT_FULL;
10466+
n->options = lappend(n->options,
10467+
makeDefElem("full", NULL, @2));
1046610468
if ($3)
10467-
n->options |= VACOPT_FREEZE;
10469+
n->options = lappend(n->options,
10470+
makeDefElem("freeze", NULL, @3));
1046810471
if ($4)
10469-
n->options |= VACOPT_VERBOSE;
10472+
n->options = lappend(n->options,
10473+
makeDefElem("verbose", NULL, @4));
1047010474
if ($5)
10471-
n->options |= VACOPT_ANALYZE;
10475+
n->options = lappend(n->options,
10476+
makeDefElem("analyze", NULL, @5));
1047210477
n->rels = $6;
10478+
n->is_vacuumcmd = true;
1047310479
$$ = (Node *)n;
1047410480
}
10475-
| VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_list
10481+
| VACUUM '(' vac_analyze_option_list ')' opt_vacuum_relation_list
1047610482
{
1047710483
VacuumStmt *n = makeNode(VacuumStmt);
10478-
n->options = VACOPT_VACUUM | $3;
10484+
n->options = $3;
1047910485
n->rels = $5;
10486+
n->is_vacuumcmd = true;
1048010487
$$ = (Node *) n;
1048110488
}
1048210489
;
1048310490

10484-
vacuum_option_list:
10485-
vacuum_option_elem { $$ = $1; }
10486-
| vacuum_option_list ',' vacuum_option_elem { $$ = $1 | $3; }
10487-
;
10488-
10489-
vacuum_option_elem:
10490-
analyze_keyword { $$ = VACOPT_ANALYZE; }
10491-
| VERBOSE { $$ = VACOPT_VERBOSE; }
10492-
| FREEZE { $$ = VACOPT_FREEZE; }
10493-
| FULL { $$ = VACOPT_FULL; }
10494-
| IDENT
10495-
{
10496-
if (strcmp($1, "disable_page_skipping") == 0)
10497-
$$ = VACOPT_DISABLE_PAGE_SKIPPING;
10498-
else if (strcmp($1, "skip_locked") == 0)
10499-
$$ = VACOPT_SKIP_LOCKED;
10500-
else
10501-
ereport(ERROR,
10502-
(errcode(ERRCODE_SYNTAX_ERROR),
10503-
errmsg("unrecognized VACUUM option \"%s\"", $1),
10504-
parser_errposition(@1)));
10505-
}
10506-
;
10507-
1050810491
AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list
1050910492
{
1051010493
VacuumStmt *n = makeNode(VacuumStmt);
10511-
n->options = VACOPT_ANALYZE;
10494+
n->options = NIL;
1051210495
if ($2)
10513-
n->options |= VACOPT_VERBOSE;
10496+
n->options = lappend(n->options,
10497+
makeDefElem("verbose", NULL, @2));
1051410498
n->rels = $3;
10499+
n->is_vacuumcmd = false;
1051510500
$$ = (Node *)n;
1051610501
}
10517-
| analyze_keyword '(' analyze_option_list ')' opt_vacuum_relation_list
10502+
| analyze_keyword '(' vac_analyze_option_list ')' opt_vacuum_relation_list
1051810503
{
1051910504
VacuumStmt *n = makeNode(VacuumStmt);
10520-
n->options = VACOPT_ANALYZE | $3;
10505+
n->options = $3;
1052110506
n->rels = $5;
10507+
n->is_vacuumcmd = false;
1052210508
$$ = (Node *) n;
1052310509
}
1052410510
;
1052510511

10526-
analyze_option_list:
10527-
analyze_option_elem { $$ = $1; }
10528-
| analyze_option_list ',' analyze_option_elem { $$ = $1 | $3; }
10529-
;
10530-
10531-
analyze_option_elem:
10532-
VERBOSE { $$ = VACOPT_VERBOSE; }
10533-
| IDENT
10512+
vac_analyze_option_list:
10513+
vac_analyze_option_elem
1053410514
{
10535-
if (strcmp($1, "skip_locked") == 0)
10536-
$$ = VACOPT_SKIP_LOCKED;
10537-
else
10538-
ereport(ERROR,
10539-
(errcode(ERRCODE_SYNTAX_ERROR),
10540-
errmsg("unrecognized ANALYZE option \"%s\"", $1),
10541-
parser_errposition(@1)));
10515+
$$ = list_make1($1);
10516+
}
10517+
| vac_analyze_option_list ',' vac_analyze_option_elem
10518+
{
10519+
$$ = lappend($1, $3);
1054210520
}
1054310521
;
1054410522

@@ -10547,6 +10525,18 @@ analyze_keyword:
1054710525
| ANALYSE /* British */ {}
1054810526
;
1054910527

10528+
vac_analyze_option_elem:
10529+
vac_analyze_option_name
10530+
{
10531+
$$ = makeDefElem($1, NULL, @1);
10532+
}
10533+
;
10534+
10535+
vac_analyze_option_name:
10536+
NonReservedWord { $$ = $1; }
10537+
| analyze_keyword { $$ = "analyze"; }
10538+
;
10539+
1055010540
opt_analyze:
1055110541
analyze_keyword { $$ = true; }
1055210542
| /*EMPTY*/ { $$ = false; }

src/backend/tcop/utility.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -664,10 +664,10 @@ standard_ProcessUtility(PlannedStmt *pstmt,
664664
VacuumStmt *stmt = (VacuumStmt *) parsetree;
665665

666666
/* we choose to allow this during "read only" transactions */
667-
PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ?
667+
PreventCommandDuringRecovery(stmt->is_vacuumcmd ?
668668
"VACUUM" : "ANALYZE");
669669
/* forbidden in parallel mode due to CommandIsReadOnly */
670-
ExecVacuum(stmt, isTopLevel);
670+
ExecVacuum(pstate, stmt, isTopLevel);
671671
}
672672
break;
673673

@@ -2570,7 +2570,7 @@ CreateCommandTag(Node *parsetree)
25702570
break;
25712571

25722572
case T_VacuumStmt:
2573-
if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
2573+
if (((VacuumStmt *) parsetree)->is_vacuumcmd)
25742574
tag = "VACUUM";
25752575
else
25762576
tag = "ANALYZE";

src/include/commands/vacuum.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,23 @@ typedef struct VacAttrStats
136136
int rowstride;
137137
} VacAttrStats;
138138

139+
typedef enum VacuumOption
140+
{
141+
VACOPT_VACUUM = 1 << 0, /* do VACUUM */
142+
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
143+
VACOPT_VERBOSE = 1 << 2, /* print progress info */
144+
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
145+
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
146+
VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */
147+
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
148+
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
149+
} VacuumOption;
150+
139151
/*
140152
* Parameters customizing behavior of VACUUM and ANALYZE.
153+
*
154+
* Note that at least one of VACOPT_VACUUM and VACOPT_ANALYZE must be set
155+
* in options.
141156
*/
142157
typedef struct VacuumParams
143158
{
@@ -163,7 +178,7 @@ extern int vacuum_multixact_freeze_table_age;
163178

164179

165180
/* in commands/vacuum.c */
166-
extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel);
181+
extern void ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel);
167182
extern void vacuum(List *relations, VacuumParams *params,
168183
BufferAccessStrategy bstrategy, bool isTopLevel);
169184
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,

src/include/nodes/parsenodes.h

+7-19
Original file line numberDiff line numberDiff line change
@@ -3151,21 +3151,16 @@ typedef struct ClusterStmt
31513151
* Vacuum and Analyze Statements
31523152
*
31533153
* Even though these are nominally two statements, it's convenient to use
3154-
* just one node type for both. Note that at least one of VACOPT_VACUUM
3155-
* and VACOPT_ANALYZE must be set in options.
3154+
* just one node type for both.
31563155
* ----------------------
31573156
*/
3158-
typedef enum VacuumOption
3157+
typedef struct VacuumStmt
31593158
{
3160-
VACOPT_VACUUM = 1 << 0, /* do VACUUM */
3161-
VACOPT_ANALYZE = 1 << 1, /* do ANALYZE */
3162-
VACOPT_VERBOSE = 1 << 2, /* print progress info */
3163-
VACOPT_FREEZE = 1 << 3, /* FREEZE option */
3164-
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
3165-
VACOPT_SKIP_LOCKED = 1 << 5, /* skip if cannot get lock */
3166-
VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */
3167-
VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */
3168-
} VacuumOption;
3159+
NodeTag type;
3160+
List *options; /* list of DefElem nodes */
3161+
List *rels; /* list of VacuumRelation, or NIL for all */
3162+
bool is_vacuumcmd; /* true for VACUUM, false for ANALYZE */
3163+
} VacuumStmt;
31693164

31703165
/*
31713166
* Info about a single target table of VACUUM/ANALYZE.
@@ -3182,13 +3177,6 @@ typedef struct VacuumRelation
31823177
List *va_cols; /* list of column names, or NIL for all */
31833178
} VacuumRelation;
31843179

3185-
typedef struct VacuumStmt
3186-
{
3187-
NodeTag type;
3188-
int options; /* OR of VacuumOption flags */
3189-
List *rels; /* list of VacuumRelation, or NIL for all */
3190-
} VacuumStmt;
3191-
31923180
/* ----------------------
31933181
* Explain Statement
31943182
*

src/test/regress/expected/vacuum.out

+5-1
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ ERROR: column "does_not_exist" of relation "vacparted" does not exist
116116
ANALYZE (VERBOSE) does_not_exist;
117117
ERROR: relation "does_not_exist" does not exist
118118
ANALYZE (nonexistent-arg) does_not_exist;
119-
ERROR: unrecognized ANALYZE option "nonexistent"
119+
ERROR: syntax error at or near "-"
120120
LINE 1: ANALYZE (nonexistent-arg) does_not_exist;
121+
^
122+
ANALYZE (nonexistentarg) does_not_exit;
123+
ERROR: unrecognized ANALYZE option "nonexistentarg"
124+
LINE 1: ANALYZE (nonexistentarg) does_not_exit;
121125
^
122126
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
123127
-- relation still errors out.

src/test/regress/sql/vacuum.sql

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ ANALYZE vactst (i), vacparted (does_not_exist);
9292
-- parenthesized syntax for ANALYZE
9393
ANALYZE (VERBOSE) does_not_exist;
9494
ANALYZE (nonexistent-arg) does_not_exist;
95+
ANALYZE (nonexistentarg) does_not_exit;
9596

9697
-- ensure argument order independence, and that SKIP_LOCKED on non-existing
9798
-- relation still errors out.

0 commit comments

Comments
 (0)