diff --git a/doc/src/sgml/ref/cluster.sgml b/doc/src/sgml/ref/cluster.sgml
index b9450e7366..5dd21a0189 100644
--- a/doc/src/sgml/ref/cluster.sgml
+++ b/doc/src/sgml/ref/cluster.sgml
@@ -22,7 +22,13 @@ PostgreSQL documentation
CLUSTER [VERBOSE] table_name [ USING index_name ]
+CLUSTER ( option [, ...] ) table_name [ USING index_name ]
CLUSTER [VERBOSE]
+
+where option can be one of:
+
+ VERBOSE [ boolean ]
+
@@ -107,6 +113,20 @@ CLUSTER [VERBOSE]
+
+
+ boolean
+
+
+ Specifies whether the selected option should be turned on or off.
+ You can write TRUE, ON, or
+ 1 to enable the option, and FALSE,
+ OFF, or 0 to disable it. The
+ boolean value can also
+ be omitted, in which case TRUE is assumed.
+
+
+
diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml
index f6d425a691..6e1cf06713 100644
--- a/doc/src/sgml/ref/reindex.sgml
+++ b/doc/src/sgml/ref/reindex.sgml
@@ -25,7 +25,8 @@ REINDEX [ ( option [, ...] ) ] { IN
where option can be one of:
- VERBOSE
+ CONCURRENTLY [ boolean ]
+ VERBOSE [ boolean ]
@@ -194,6 +195,21 @@ REINDEX [ ( option [, ...] ) ] { IN
+
+
+ boolean
+
+
+ Specifies whether the selected option should be turned on or off.
+ You can write TRUE, ON, or
+ 1 to enable the option, and FALSE,
+ OFF, or 0 to disable it. The
+ boolean value can also
+ be omitted, in which case TRUE is assumed.
+
+
+
+
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 04d12a7ece..fd5a6eec86 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -35,6 +35,7 @@
#include "catalog/pg_am.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
+#include "commands/defrem.h"
#include "commands/progress.h"
#include "commands/tablecmds.h"
#include "commands/vacuum.h"
@@ -99,8 +100,29 @@ static List *get_tables_to_cluster(MemoryContext cluster_context);
*---------------------------------------------------------------------------
*/
void
-cluster(ClusterStmt *stmt, bool isTopLevel)
+cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel)
{
+ ListCell *lc;
+ int options = 0;
+ bool verbose = false;
+
+ /* Parse option list */
+ foreach(lc, stmt->params)
+ {
+ DefElem *opt = (DefElem *) lfirst(lc);
+
+ if (strcmp(opt->defname, "verbose") == 0)
+ verbose = defGetBoolean(opt);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized CLUSTER option \"%s\"",
+ opt->defname),
+ parser_errposition(pstate, opt->location)));
+ }
+
+ options = (verbose ? CLUOPT_VERBOSE : 0);
+
if (stmt->relation != NULL)
{
/* This is the single-relation case. */
@@ -170,7 +192,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
table_close(rel, NoLock);
/* Do the job. */
- cluster_rel(tableOid, indexOid, stmt->options);
+ cluster_rel(tableOid, indexOid, options);
}
else
{
@@ -219,7 +241,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
PushActiveSnapshot(GetTransactionSnapshot());
/* Do the job. */
cluster_rel(rvtc->tableOid, rvtc->indexOid,
- stmt->options | CLUOPT_RECHECK);
+ options | CLUOPT_RECHECK);
PopActiveSnapshot();
CommitTransactionCommand();
}
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d35deb433a..14d24b3cc4 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -2451,6 +2451,42 @@ ChooseIndexColumnNames(List *indexElems)
return result;
}
+/*
+ * ReindexParseOptions
+ * Parse list of REINDEX options, returning a bitmask of ReindexOption.
+ */
+int
+ReindexParseOptions(ParseState *pstate, ReindexStmt *stmt)
+{
+ ListCell *lc;
+ int options = 0;
+ bool concurrently = false;
+ bool verbose = false;
+
+ /* Parse option list */
+ foreach(lc, stmt->params)
+ {
+ DefElem *opt = (DefElem *) lfirst(lc);
+
+ if (strcmp(opt->defname, "verbose") == 0)
+ verbose = defGetBoolean(opt);
+ else if (strcmp(opt->defname, "concurrently") == 0)
+ concurrently = defGetBoolean(opt);
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized REINDEX option \"%s\"",
+ opt->defname),
+ parser_errposition(pstate, opt->location)));
+ }
+
+ options =
+ (verbose ? REINDEXOPT_VERBOSE : 0) |
+ (concurrently ? REINDEXOPT_CONCURRENTLY : 0);
+
+ return options;
+}
+
/*
* ReindexIndex
* Recreate a specific index.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 74882597f9..910906f639 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3349,7 +3349,7 @@ _copyClusterStmt(const ClusterStmt *from)
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(indexname);
- COPY_SCALAR_FIELD(options);
+ COPY_NODE_FIELD(params);
return newnode;
}
@@ -4443,7 +4443,7 @@ _copyReindexStmt(const ReindexStmt *from)
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(relation);
COPY_STRING_FIELD(name);
- COPY_SCALAR_FIELD(options);
+ COPY_NODE_FIELD(params);
return newnode;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e69815d1c5..687609f59e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1210,7 +1210,7 @@ _equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
{
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(indexname);
- COMPARE_SCALAR_FIELD(options);
+ COMPARE_NODE_FIELD(params);
return true;
}
@@ -2130,7 +2130,7 @@ _equalReindexStmt(const ReindexStmt *a, const ReindexStmt *b)
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(relation);
COMPARE_STRING_FIELD(name);
- COMPARE_SCALAR_FIELD(options);
+ COMPARE_NODE_FIELD(params);
return true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cbd5fa3cc0..61f0236041 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -515,7 +515,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type generic_option_list alter_generic_option_list
%type reindex_target_type reindex_target_multitable
-%type reindex_option_list reindex_option_elem
%type copy_generic_opt_arg copy_generic_opt_arg_list_item
%type copy_generic_opt_elem
@@ -8217,9 +8216,10 @@ ReindexStmt:
n->kind = $2;
n->relation = $4;
n->name = NULL;
- n->options = 0;
+ n->params = NIL;
if ($3)
- n->options |= REINDEXOPT_CONCURRENTLY;
+ n->params = lappend(n->params,
+ makeDefElem("concurrently", NULL, @3));
$$ = (Node *)n;
}
| REINDEX reindex_target_multitable opt_concurrently name
@@ -8228,31 +8228,34 @@ ReindexStmt:
n->kind = $2;
n->name = $4;
n->relation = NULL;
- n->options = 0;
+ n->params = NIL;
if ($3)
- n->options |= REINDEXOPT_CONCURRENTLY;
+ n->params = lappend(n->params,
+ makeDefElem("concurrently", NULL, @3));
$$ = (Node *)n;
}
- | REINDEX '(' reindex_option_list ')' reindex_target_type opt_concurrently qualified_name
+ | REINDEX '(' utility_option_list ')' reindex_target_type opt_concurrently qualified_name
{
ReindexStmt *n = makeNode(ReindexStmt);
n->kind = $5;
n->relation = $7;
n->name = NULL;
- n->options = $3;
+ n->params = $3;
if ($6)
- n->options |= REINDEXOPT_CONCURRENTLY;
+ n->params = lappend(n->params,
+ makeDefElem("concurrently", NULL, @6));
$$ = (Node *)n;
}
- | REINDEX '(' reindex_option_list ')' reindex_target_multitable opt_concurrently name
+ | REINDEX '(' utility_option_list ')' reindex_target_multitable opt_concurrently name
{
ReindexStmt *n = makeNode(ReindexStmt);
n->kind = $5;
n->name = $7;
n->relation = NULL;
- n->options = $3;
+ n->params = $3;
if ($6)
- n->options |= REINDEXOPT_CONCURRENTLY;
+ n->params = lappend(n->params,
+ makeDefElem("concurrently", NULL, @6));
$$ = (Node *)n;
}
;
@@ -8265,13 +8268,6 @@ reindex_target_multitable:
| SYSTEM_P { $$ = REINDEX_OBJECT_SYSTEM; }
| DATABASE { $$ = REINDEX_OBJECT_DATABASE; }
;
-reindex_option_list:
- reindex_option_elem { $$ = $1; }
- | reindex_option_list ',' reindex_option_elem { $$ = $1 | $3; }
- ;
-reindex_option_elem:
- VERBOSE { $$ = REINDEXOPT_VERBOSE; }
- ;
/*****************************************************************************
*
@@ -10407,6 +10403,7 @@ CreateConversionStmt:
*
* QUERY:
* CLUSTER [VERBOSE] [ USING ]
+ * CLUSTER [ (options) ] [ USING ]
* CLUSTER [VERBOSE]
* CLUSTER [VERBOSE] ON (for pre-8.3)
*
@@ -10418,9 +10415,18 @@ ClusterStmt:
ClusterStmt *n = makeNode(ClusterStmt);
n->relation = $3;
n->indexname = $4;
- n->options = 0;
+ n->params = NIL;
if ($2)
- n->options |= CLUOPT_VERBOSE;
+ n->params = lappend(n->params, makeDefElem("verbose", NULL, @2));
+ $$ = (Node*)n;
+ }
+
+ | CLUSTER '(' utility_option_list ')' qualified_name cluster_index_specification
+ {
+ ClusterStmt *n = makeNode(ClusterStmt);
+ n->relation = $5;
+ n->indexname = $6;
+ n->params = $3;
$$ = (Node*)n;
}
| CLUSTER opt_verbose
@@ -10428,9 +10434,9 @@ ClusterStmt:
ClusterStmt *n = makeNode(ClusterStmt);
n->relation = NULL;
n->indexname = NULL;
- n->options = 0;
+ n->params = NIL;
if ($2)
- n->options |= CLUOPT_VERBOSE;
+ n->params = lappend(n->params, makeDefElem("verbose", NULL, @2));
$$ = (Node*)n;
}
/* kept for pre-8.3 compatibility */
@@ -10439,9 +10445,9 @@ ClusterStmt:
ClusterStmt *n = makeNode(ClusterStmt);
n->relation = $5;
n->indexname = $3;
- n->options = 0;
+ n->params = NIL;
if ($2)
- n->options |= CLUOPT_VERBOSE;
+ n->params = lappend(n->params, makeDefElem("verbose", NULL, @2));
$$ = (Node*)n;
}
;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 81ac9b1cb2..a42ead7d69 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -22,6 +22,7 @@
#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
+#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_inherits.h"
#include "catalog/toasting.h"
@@ -818,7 +819,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
break;
case T_ClusterStmt:
- cluster((ClusterStmt *) parsetree, isTopLevel);
+ cluster(pstate, (ClusterStmt *) parsetree, isTopLevel);
break;
case T_VacuumStmt:
@@ -918,20 +919,20 @@ standard_ProcessUtility(PlannedStmt *pstmt,
case T_ReindexStmt:
{
ReindexStmt *stmt = (ReindexStmt *) parsetree;
+ int options;
- if ((stmt->options & REINDEXOPT_CONCURRENTLY) != 0)
+ options = ReindexParseOptions(pstate, stmt);
+ if ((options & REINDEXOPT_CONCURRENTLY) != 0)
PreventInTransactionBlock(isTopLevel,
"REINDEX CONCURRENTLY");
switch (stmt->kind)
{
case REINDEX_OBJECT_INDEX:
- ReindexIndex(stmt->relation, stmt->options,
- isTopLevel);
+ ReindexIndex(stmt->relation, options, isTopLevel);
break;
case REINDEX_OBJECT_TABLE:
- ReindexTable(stmt->relation, stmt->options,
- isTopLevel);
+ ReindexTable(stmt->relation, options, isTopLevel);
break;
case REINDEX_OBJECT_SCHEMA:
case REINDEX_OBJECT_SYSTEM:
@@ -947,7 +948,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
(stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
(stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
"REINDEX DATABASE");
- ReindexMultipleTables(stmt->name, stmt->kind, stmt->options);
+ ReindexMultipleTables(stmt->name, stmt->kind, options);
break;
default:
elog(ERROR, "unrecognized object type: %d",
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 8afc780acc..3a43c09bf6 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -2287,21 +2287,33 @@ psql_completion(const char *text, int start, int end)
/* CLUSTER */
else if (Matches("CLUSTER"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, "UNION SELECT 'VERBOSE'");
- else if (Matches("CLUSTER", "VERBOSE"))
+ else if (Matches("CLUSTER", "VERBOSE") ||
+ Matches("CLUSTER", "(*)"))
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_clusterables, NULL);
/* If we have CLUSTER , then add "USING" */
- else if (Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON")))
+ else if (Matches("CLUSTER", MatchAnyExcept("VERBOSE|ON|(|(*)")))
COMPLETE_WITH("USING");
/* If we have CLUSTER VERBOSE , then add "USING" */
- else if (Matches("CLUSTER", "VERBOSE", MatchAny))
+ else if (Matches("CLUSTER", "VERBOSE|(*)", MatchAny))
COMPLETE_WITH("USING");
/* If we have CLUSTER USING, then add the index as well */
else if (Matches("CLUSTER", MatchAny, "USING") ||
- Matches("CLUSTER", "VERBOSE", MatchAny, "USING"))
+ Matches("CLUSTER", "VERBOSE|(*)", MatchAny, "USING"))
{
completion_info_charp = prev2_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
+ else if (HeadMatches("CLUSTER", "(*") &&
+ !HeadMatches("CLUSTER", "(*)"))
+ {
+ /*
+ * This fires if we're in an unfinished parenthesized option list.
+ * get_previous_words treats a completed parenthesized option list as
+ * one word, so the above test is correct.
+ */
+ if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+ COMPLETE_WITH("VERBOSE");
+ }
/* COMMENT */
else if (Matches("COMMENT"))
@@ -3565,7 +3577,7 @@ psql_completion(const char *text, int start, int end)
* one word, so the above test is correct.
*/
if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
- COMPLETE_WITH("VERBOSE");
+ COMPLETE_WITH("CONCURRENTLY", "VERBOSE");
}
/* SECURITY LABEL */
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index f4559b09d7..c041628049 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -29,6 +29,15 @@ typedef enum
INDEX_DROP_SET_DEAD
} IndexStateFlagsAction;
+/* options for REINDEX */
+typedef enum ReindexOption
+{
+ REINDEXOPT_VERBOSE = 1 << 0, /* print progress info */
+ REINDEXOPT_REPORT_PROGRESS = 1 << 1, /* report pgstat progress */
+ REINDEXOPT_MISSING_OK = 1 << 2, /* skip missing relations */
+ REINDEXOPT_CONCURRENTLY = 1 << 3 /* concurrent mode */
+} ReindexOption;
+
/* state info for validate_index bulkdelete callback */
typedef struct ValidateIndexState
{
diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h
index e05884781b..7cfb37c9b2 100644
--- a/src/include/commands/cluster.h
+++ b/src/include/commands/cluster.h
@@ -14,11 +14,19 @@
#define CLUSTER_H
#include "nodes/parsenodes.h"
+#include "parser/parse_node.h"
#include "storage/lock.h"
#include "utils/relcache.h"
-extern void cluster(ClusterStmt *stmt, bool isTopLevel);
+/* options for CLUSTER */
+typedef enum ClusterOption
+{
+ CLUOPT_RECHECK = 1 << 0, /* recheck relation state */
+ CLUOPT_VERBOSE = 1 << 1 /* print progress info */
+} ClusterOption;
+
+extern void cluster(ParseState *pstate, ClusterStmt *stmt, bool isTopLevel);
extern void cluster_rel(Oid tableOid, Oid indexOid, int options);
extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
bool recheck, LOCKMODE lockmode);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 7a079ef07f..1133ae1143 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -34,6 +34,7 @@ extern ObjectAddress DefineIndex(Oid relationId,
bool check_not_in_use,
bool skip_build,
bool quiet);
+extern int ReindexParseOptions(ParseState *pstate, ReindexStmt *stmt);
extern void ReindexIndex(RangeVar *indexRelation, int options, bool isTopLevel);
extern Oid ReindexTable(RangeVar *relation, int options, bool isTopLevel);
extern void ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 92df76c2d3..ec14fc2036 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3197,18 +3197,12 @@ typedef struct AlterSystemStmt
* Cluster Statement (support pbrown's cluster index implementation)
* ----------------------
*/
-typedef enum ClusterOption
-{
- CLUOPT_RECHECK = 1 << 0, /* recheck relation state */
- CLUOPT_VERBOSE = 1 << 1 /* print progress info */
-} ClusterOption;
-
typedef struct ClusterStmt
{
NodeTag type;
RangeVar *relation; /* relation being indexed, or NULL if all */
char *indexname; /* original index defined */
- int options; /* OR of ClusterOption flags */
+ List *params; /* list of DefElem nodes */
} ClusterStmt;
/* ----------------------
@@ -3346,13 +3340,6 @@ typedef struct ConstraintsSetStmt
* REINDEX Statement
* ----------------------
*/
-
-/* Reindex options */
-#define REINDEXOPT_VERBOSE (1 << 0) /* print progress info */
-#define REINDEXOPT_REPORT_PROGRESS (1 << 1) /* report pgstat progress */
-#define REINDEXOPT_MISSING_OK (1 << 2) /* skip missing relations */
-#define REINDEXOPT_CONCURRENTLY (1 << 3) /* concurrent mode */
-
typedef enum ReindexObjectType
{
REINDEX_OBJECT_INDEX, /* index */
@@ -3369,7 +3356,7 @@ typedef struct ReindexStmt
* etc. */
RangeVar *relation; /* Table or index to reindex */
const char *name; /* name of database to reindex */
- int options; /* Reindex options flags */
+ List *params; /* list of DefElem nodes */
} ReindexStmt;
/* ----------------------
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index 18bb92b810..fc6afab58a 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2024,7 +2024,7 @@ CREATE TABLE concur_reindex_tab (c1 int);
-- REINDEX
REINDEX TABLE concur_reindex_tab; -- notice
NOTICE: table "concur_reindex_tab" has no indexes to reindex
-REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice
+REINDEX (CONCURRENTLY) TABLE concur_reindex_tab; -- notice
NOTICE: table "concur_reindex_tab" has no indexes that can be reindexed concurrently
ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index
-- Normal index with integer column
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 55326eb47b..824cb9f9e8 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -796,7 +796,7 @@ DROP TABLE reindex_verbose;
CREATE TABLE concur_reindex_tab (c1 int);
-- REINDEX
REINDEX TABLE concur_reindex_tab; -- notice
-REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice
+REINDEX (CONCURRENTLY) TABLE concur_reindex_tab; -- notice
ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index
-- Normal index with integer column
CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab(c1);