Revise parse tree representation for VACUUM and ANALYZE.
authorRobert Haas <rhaas@postgresql.org>
Mon, 18 Mar 2019 19:14:52 +0000 (15:14 -0400)
committerRobert Haas <rhaas@postgresql.org>
Mon, 18 Mar 2019 19:14:52 +0000 (15:14 -0400)
Like commit f41551f61f9cf4eedd5b7173f985a3bdb4d9858c, 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

src/backend/commands/vacuum.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/include/commands/vacuum.h
src/include/nodes/parsenodes.h
src/test/regress/expected/vacuum.out
src/test/regress/sql/vacuum.sql

index be03185a7f7d7d4d813ce18d0efe764cb7d1b856..f0afeaf40c27eeca9d6f0d1b5ab08528c84ecffb 100644 (file)
@@ -83,20 +83,55 @@ static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
  * happen in vacuum().
  */
 void
-ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
+ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 {
        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 */
-       Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
-       Assert((vacstmt->options & VACOPT_VACUUM) ||
-                  !(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
-       Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
+       Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
+       Assert((params.options & VACOPT_VACUUM) ||
+                  !(params.options & (VACOPT_FULL | VACOPT_FREEZE)));
+       Assert(!(params.options & VACOPT_SKIPTOAST));
 
        /*
         * Make sure VACOPT_ANALYZE is specified if any column lists are present.
         */
-       if (!(vacstmt->options & VACOPT_ANALYZE))
+       if (!(params.options & VACOPT_ANALYZE))
        {
                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
         * 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_table_age = 0;
index e23e68fdb332caa2b48c430a4242e871446a3f85..e814939a254ff48d83ffaf3b23f851f44cad7254 100644 (file)
@@ -306,8 +306,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                                create_extension_opt_item alter_extension_opt_item
 
 %type <ival>   opt_lock lock_type cast_context
-%type <ival>   vacuum_option_list vacuum_option_elem
-                               analyze_option_list analyze_option_elem
+%type <str>            vac_analyze_option_name
+%type <defelt> vac_analyze_option_elem
+%type <list>   vac_analyze_option_list
 %type <boolean>        opt_or_replace
                                opt_grant_grant_option opt_grant_admin_option
                                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 *n = makeNode(VacuumStmt);
-                                       n->options = VACOPT_VACUUM;
+                                       n->options = NIL;
                                        if ($2)
-                                               n->options |= VACOPT_FULL;
+                                               n->options = lappend(n->options,
+                                                                                        makeDefElem("full", NULL, @2));
                                        if ($3)
-                                               n->options |= VACOPT_FREEZE;
+                                               n->options = lappend(n->options,
+                                                                                        makeDefElem("freeze", NULL, @3));
                                        if ($4)
-                                               n->options |= VACOPT_VERBOSE;
+                                               n->options = lappend(n->options,
+                                                                                        makeDefElem("verbose", NULL, @4));
                                        if ($5)
-                                               n->options |= VACOPT_ANALYZE;
+                                               n->options = lappend(n->options,
+                                                                                        makeDefElem("analyze", NULL, @5));
                                        n->rels = $6;
+                                       n->is_vacuumcmd = true;
                                        $$ = (Node *)n;
                                }
-                       | VACUUM '(' vacuum_option_list ')' opt_vacuum_relation_list
+                       | VACUUM '(' vac_analyze_option_list ')' opt_vacuum_relation_list
                                {
                                        VacuumStmt *n = makeNode(VacuumStmt);
-                                       n->options = VACOPT_VACUUM | $3;
+                                       n->options = $3;
                                        n->rels = $5;
+                                       n->is_vacuumcmd = true;
                                        $$ = (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
                                {
                                        VacuumStmt *n = makeNode(VacuumStmt);
-                                       n->options = VACOPT_ANALYZE;
+                                       n->options = NIL;
                                        if ($2)
-                                               n->options |= VACOPT_VERBOSE;
+                                               n->options = lappend(n->options,
+                                                                                        makeDefElem("verbose", NULL, @2));
                                        n->rels = $3;
+                                       n->is_vacuumcmd = false;
                                        $$ = (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);
-                                       n->options = VACOPT_ANALYZE | $3;
+                                       n->options = $3;
                                        n->rels = $5;
+                                       n->is_vacuumcmd = false;
                                        $$ = (Node *) n;
                                }
                ;
 
-analyze_option_list:
-                       analyze_option_elem                                                             { $$ = $1; }
-                       | analyze_option_list ',' analyze_option_elem   { $$ = $1 | $3; }
-               ;
-
-analyze_option_elem:
-                       VERBOSE                         { $$ = VACOPT_VERBOSE; }
-                       | IDENT
+vac_analyze_option_list:
+                       vac_analyze_option_elem
                                {
-                                       if (strcmp($1, "skip_locked") == 0)
-                                               $$ = VACOPT_SKIP_LOCKED;
-                                       else
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                                                errmsg("unrecognized ANALYZE option \"%s\"", $1),
-                                                                        parser_errposition(@1)));
+                                       $$ = list_make1($1);
+                               }
+                       | vac_analyze_option_list ',' vac_analyze_option_elem
+                               {
+                                       $$ = lappend($1, $3);
                                }
                ;
 
@@ -10547,6 +10525,18 @@ analyze_keyword:
                        | 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:
                        analyze_keyword                                                 { $$ = true; }
                        | /*EMPTY*/                                                             { $$ = false; }
index 6ec795f1b4608cf73db7a469d449dd1ac22bcc68..bdfaa506e7e25be40975114d3b2f11fb83cc374d 100644 (file)
@@ -664,10 +664,10 @@ standard_ProcessUtility(PlannedStmt *pstmt,
                                VacuumStmt *stmt = (VacuumStmt *) parsetree;
 
                                /* we choose to allow this during "read only" transactions */
-                               PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ?
+                               PreventCommandDuringRecovery(stmt->is_vacuumcmd ?
                                                                                         "VACUUM" : "ANALYZE");
                                /* forbidden in parallel mode due to CommandIsReadOnly */
-                               ExecVacuum(stmt, isTopLevel);
+                               ExecVacuum(pstate, stmt, isTopLevel);
                        }
                        break;
 
@@ -2570,7 +2570,7 @@ CreateCommandTag(Node *parsetree)
                        break;
 
                case T_VacuumStmt:
-                       if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM)
+                       if (((VacuumStmt *) parsetree)->is_vacuumcmd)
                                tag = "VACUUM";
                        else
                                tag = "ANALYZE";
index c0df9c9054b08bc472ad9f0b4b5f7633702f937d..77086f3e9130ef41624ac34208df7ef94598b25f 100644 (file)
@@ -136,8 +136,23 @@ typedef struct VacAttrStats
        int                     rowstride;
 } 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.
+ *
+ * Note that at least one of VACOPT_VACUUM and VACOPT_ANALYZE must be set
+ * in options.
  */
 typedef struct VacuumParams
 {
@@ -163,7 +178,7 @@ extern int  vacuum_multixact_freeze_table_age;
 
 
 /* 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,
           BufferAccessStrategy bstrategy, bool isTopLevel);
 extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
index fe35783359e4dd031278307a908f893e56a891ef..fcfba4be4c84a0194dfa6eb83a1e6d323aad53cf 100644 (file)
@@ -3151,21 +3151,16 @@ typedef struct ClusterStmt
  *             Vacuum and Analyze Statements
  *
  * 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
- * and VACOPT_ANALYZE must be set in options.
+ * just one node type for both.
  * ----------------------
  */
-typedef enum VacuumOption
+typedef struct VacuumStmt
 {
-       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;
+       NodeTag         type;
+       List            *options;               /* list of DefElem nodes */
+       List       *rels;                       /* list of VacuumRelation, or NIL for all */
+       bool            is_vacuumcmd;   /* true for VACUUM, false for ANALYZE */
+} VacuumStmt;
 
 /*
  * 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 */
 } VacuumRelation;
 
-typedef struct VacuumStmt
-{
-       NodeTag         type;
-       int                     options;                /* OR of VacuumOption flags */
-       List       *rels;                       /* list of VacuumRelation, or NIL for all */
-} VacuumStmt;
-
 /* ----------------------
  *             Explain Statement
  *
index fa9d663abdaf7de2d488ddbebbb6838ec2ab6b52..07d0703115845ce4bf4229541c28b4d2e677b9ed 100644 (file)
@@ -116,8 +116,12 @@ ERROR:  column "does_not_exist" of relation "vacparted" does not exist
 ANALYZE (VERBOSE) does_not_exist;
 ERROR:  relation "does_not_exist" 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;
+                            ^
+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
 -- relation still errors out.
index 9defa0d8b2cb33be2c074327f5c15b973508dc97..81f382267964bbc10f1e1d96195a400ab3524547 100644 (file)
@@ -92,6 +92,7 @@ ANALYZE vactst (i), vacparted (does_not_exist);
 -- parenthesized syntax for ANALYZE
 ANALYZE (VERBOSE) 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
 -- relation still errors out.