Add support for REINDEX in event triggers
authorMichael Paquier <[email protected]>
Mon, 4 Dec 2023 00:53:49 +0000 (09:53 +0900)
committerMichael Paquier <[email protected]>
Mon, 4 Dec 2023 00:53:49 +0000 (09:53 +0900)
This commit adds support for REINDEX in event triggers, making this
command react for the events ddl_command_start and ddl_command_end.  The
indexes rebuilt are collected with the ReindexStmt emitted by the
caller, for the concurrent and non-concurrent paths.

Thanks to that, it is possible to know a full list of the indexes that a
single REINDEX command has worked on.

Author: Garrett Thornburg, Jian He
Reviewed-by: Jim Jones, Michael Paquier
Discussion: https://postgr.es/m/CAEEqfk5bm32G7sbhzHbES9WejD8O8DCEOaLkxoBP7HNWxjPpvg@mail.gmail.com

doc/src/sgml/event-trigger.sgml
src/backend/catalog/index.c
src/backend/commands/cluster.c
src/backend/commands/indexcmds.c
src/backend/commands/tablecmds.c
src/backend/tcop/utility.c
src/include/catalog/index.h
src/include/tcop/cmdtaglist.h
src/test/regress/expected/event_trigger.out
src/test/regress/sql/event_trigger.sql

index 10b20f0339a6263ae881d23b923c5e59512f1de9..234b4ffd02474053061005c91078f11813c4bd4c 100644 (file)
         <entry align="center"><literal>-</literal></entry>
         <entry align="left"></entry>
        </row>
+       <row>
+        <entry align="left"><literal>REINDEX</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>X</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="center"><literal>-</literal></entry>
+        <entry align="left"></entry>
+       </row>
        <row>
         <entry align="left"><literal>REVOKE</literal></entry>
         <entry align="center"><literal>X</literal></entry>
index 143fae01ebd548aaf262dca02d569ec01cf7e40a..b8c7945322e55bf9bf306da09984d4177690ccad 100644 (file)
@@ -3558,7 +3558,8 @@ IndexGetRelation(Oid indexId, bool missing_ok)
  * reindex_index - This routine is used to recreate a single index
  */
 void
-reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
+reindex_index(const ReindexStmt *stmt, Oid indexId,
+             bool skip_constraint_checks, char persistence,
              const ReindexParams *params)
 {
    Relation    iRel,
@@ -3630,6 +3631,20 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
        pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
                                     iRel->rd_rel->relam);
 
+   /*
+    * If a statement is available, telling that this comes from a REINDEX
+    * command, collect the index for event triggers.
+    */
+   if (stmt)
+   {
+       ObjectAddress address;
+
+       ObjectAddressSet(address, RelationRelationId, indexId);
+       EventTriggerCollectSimpleCommand(address,
+                                        InvalidObjectAddress,
+                                        (Node *) stmt);
+   }
+
    /*
     * Partitioned indexes should never get processed here, as they have no
     * physical storage.
@@ -3865,7 +3880,8 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
  * index rebuild.
  */
 bool
-reindex_relation(Oid relid, int flags, const ReindexParams *params)
+reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
+                const ReindexParams *params)
 {
    Relation    rel;
    Oid         toast_relid;
@@ -3953,7 +3969,7 @@ reindex_relation(Oid relid, int flags, const ReindexParams *params)
            continue;
        }
 
-       reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
+       reindex_index(stmt, indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
                      persistence, params);
 
        CommandCounterIncrement();
@@ -3990,7 +4006,7 @@ reindex_relation(Oid relid, int flags, const ReindexParams *params)
 
        newparams.options &= ~(REINDEXOPT_MISSING_OK);
        newparams.tablespaceOid = InvalidOid;
-       result |= reindex_relation(toast_relid, flags, &newparams);
+       result |= reindex_relation(stmt, toast_relid, flags, &newparams);
    }
 
    return result;
index a3bef6ac34f61436fbf6beb8fb28fd5ba7655c69..1f52d3913f704bef35ea4ccb5eaa01871e2c5066 100644 (file)
@@ -1518,7 +1518,7 @@ finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
    pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
                                 PROGRESS_CLUSTER_PHASE_REBUILD_INDEX);
 
-   reindex_relation(OIDOldHeap, reindex_flags, &reindex_params);
+   reindex_relation(NULL, OIDOldHeap, reindex_flags, &reindex_params);
 
    /* Report that we are now doing clean up */
    pgstat_progress_update_param(PROGRESS_CLUSTER_PHASE,
index 795f29d02d4f00e8fbacb39e73c1be9419d0cdf4..412e1ba84f4915c59440d680a6198530600f0f04 100644 (file)
@@ -94,20 +94,21 @@ static char *ChooseIndexName(const char *tabname, Oid namespaceId,
                             bool primary, bool isconstraint);
 static char *ChooseIndexNameAddition(const List *colnames);
 static List *ChooseIndexColumnNames(const List *indexElems);
-static void ReindexIndex(const RangeVar *indexRelation, const ReindexParams *params,
+static void ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params,
                         bool isTopLevel);
 static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
                                            Oid relId, Oid oldRelId, void *arg);
-static Oid ReindexTable(const RangeVar *relation, const ReindexParams *params,
+static Oid ReindexTable(const ReindexStmt *stmt, const ReindexParams *params,
                         bool isTopLevel);
-static void ReindexMultipleTables(const char *objectName,
-                                 ReindexObjectType objectKind, const ReindexParams *params);
+static void ReindexMultipleTables(const ReindexStmt *stmt,
+                                 const ReindexParams *params);
 static void reindex_error_callback(void *arg);
-static void ReindexPartitions(Oid relid, const ReindexParams *params,
-                             bool isTopLevel);
-static void ReindexMultipleInternal(const List *relids,
+static void ReindexPartitions(const ReindexStmt *stmt, Oid relid,
+                             const ReindexParams *params, bool isTopLevel);
+static void ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids,
                                    const ReindexParams *params);
-static bool ReindexRelationConcurrently(Oid relationOid,
+static bool ReindexRelationConcurrently(const ReindexStmt *stmt,
+                                       Oid relationOid,
                                        const ReindexParams *params);
 static void update_relispartition(Oid relationId, bool newval);
 static inline void set_indexsafe_procflags(void);
@@ -2735,10 +2736,10 @@ ExecReindex(ParseState *pstate, const ReindexStmt *stmt, bool isTopLevel)
    switch (stmt->kind)
    {
        case REINDEX_OBJECT_INDEX:
-           ReindexIndex(stmt->relation, &params, isTopLevel);
+           ReindexIndex(stmt, &params, isTopLevel);
            break;
        case REINDEX_OBJECT_TABLE:
-           ReindexTable(stmt->relation, &params, isTopLevel);
+           ReindexTable(stmt, &params, isTopLevel);
            break;
        case REINDEX_OBJECT_SCHEMA:
        case REINDEX_OBJECT_SYSTEM:
@@ -2754,7 +2755,7 @@ ExecReindex(ParseState *pstate, const ReindexStmt *stmt, bool isTopLevel)
                                      (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
                                      (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
                                      "REINDEX DATABASE");
-           ReindexMultipleTables(stmt->name, stmt->kind, &params);
+           ReindexMultipleTables(stmt, &params);
            break;
        default:
            elog(ERROR, "unrecognized object type: %d",
@@ -2768,8 +2769,9 @@ ExecReindex(ParseState *pstate, const ReindexStmt *stmt, bool isTopLevel)
  *     Recreate a specific index.
  */
 static void
-ReindexIndex(const RangeVar *indexRelation, const ReindexParams *params, bool isTopLevel)
+ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
 {
+   const RangeVar *indexRelation = stmt->relation;
    struct ReindexIndexCallbackState state;
    Oid         indOid;
    char        persistence;
@@ -2802,16 +2804,16 @@ ReindexIndex(const RangeVar *indexRelation, const ReindexParams *params, bool is
    relkind = get_rel_relkind(indOid);
 
    if (relkind == RELKIND_PARTITIONED_INDEX)
-       ReindexPartitions(indOid, params, isTopLevel);
+       ReindexPartitions(stmt, indOid, params, isTopLevel);
    else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
             persistence != RELPERSISTENCE_TEMP)
-       ReindexRelationConcurrently(indOid, params);
+       ReindexRelationConcurrently(stmt, indOid, params);
    else
    {
        ReindexParams newparams = *params;
 
        newparams.options |= REINDEXOPT_REPORT_PROGRESS;
-       reindex_index(indOid, false, persistence, &newparams);
+       reindex_index(stmt, indOid, false, persistence, &newparams);
    }
 }
 
@@ -2891,10 +2893,11 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
  *     Recreate all indexes of a table (and of its toast table, if any)
  */
 static Oid
-ReindexTable(const RangeVar *relation, const ReindexParams *params, bool isTopLevel)
+ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
 {
    Oid         heapOid;
    bool        result;
+   const RangeVar *relation = stmt->relation;
 
    /*
     * The lock level used here should match reindex_relation().
@@ -2911,11 +2914,11 @@ ReindexTable(const RangeVar *relation, const ReindexParams *params, bool isTopLe
                                       RangeVarCallbackOwnsTable, NULL);
 
    if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
-       ReindexPartitions(heapOid, params, isTopLevel);
+       ReindexPartitions(stmt, heapOid, params, isTopLevel);
    else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
             get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
    {
-       result = ReindexRelationConcurrently(heapOid, params);
+       result = ReindexRelationConcurrently(stmt, heapOid, params);
 
        if (!result)
            ereport(NOTICE,
@@ -2927,7 +2930,7 @@ ReindexTable(const RangeVar *relation, const ReindexParams *params, bool isTopLe
        ReindexParams newparams = *params;
 
        newparams.options |= REINDEXOPT_REPORT_PROGRESS;
-       result = reindex_relation(heapOid,
+       result = reindex_relation(stmt, heapOid,
                                  REINDEX_REL_PROCESS_TOAST |
                                  REINDEX_REL_CHECK_CONSTRAINTS,
                                  &newparams);
@@ -2949,9 +2952,9 @@ ReindexTable(const RangeVar *relation, const ReindexParams *params, bool isTopLe
  * That means this must not be called within a user transaction block!
  */
 static void
-ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
-                     const ReindexParams *params)
+ReindexMultipleTables(const ReindexStmt *stmt, const ReindexParams *params)
 {
+
    Oid         objectOid;
    Relation    relationRelation;
    TableScanDesc scan;
@@ -2963,6 +2966,8 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
    int         num_keys;
    bool        concurrent_warning = false;
    bool        tablespace_warning = false;
+   const char *objectName = stmt->name;
+   const ReindexObjectType objectKind = stmt->kind;
 
    Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
           objectKind == REINDEX_OBJECT_SYSTEM ||
@@ -3158,7 +3163,7 @@ ReindexMultipleTables(const char *objectName, ReindexObjectType objectKind,
     * Process each relation listed in a separate transaction.  Note that this
     * commits and then starts a new transaction immediately.
     */
-   ReindexMultipleInternal(relids, params);
+   ReindexMultipleInternal(stmt, relids, params);
 
    MemoryContextDelete(private_context);
 }
@@ -3188,7 +3193,7 @@ reindex_error_callback(void *arg)
  * by the caller.
  */
 static void
-ReindexPartitions(Oid relid, const ReindexParams *params, bool isTopLevel)
+ReindexPartitions(const ReindexStmt *stmt, Oid relid, const ReindexParams *params, bool isTopLevel)
 {
    List       *partitions = NIL;
    char        relkind = get_rel_relkind(relid);
@@ -3264,7 +3269,7 @@ ReindexPartitions(Oid relid, const ReindexParams *params, bool isTopLevel)
     * Process each partition listed in a separate transaction.  Note that
     * this commits and then starts a new transaction immediately.
     */
-   ReindexMultipleInternal(partitions, params);
+   ReindexMultipleInternal(stmt, partitions, params);
 
    /*
     * Clean up working storage --- note we must do this after
@@ -3282,7 +3287,7 @@ ReindexPartitions(Oid relid, const ReindexParams *params, bool isTopLevel)
  * and starts a new transaction when finished.
  */
 static void
-ReindexMultipleInternal(const List *relids, const ReindexParams *params)
+ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const ReindexParams *params)
 {
    ListCell   *l;
 
@@ -3341,7 +3346,7 @@ ReindexMultipleInternal(const List *relids, const ReindexParams *params)
            ReindexParams newparams = *params;
 
            newparams.options |= REINDEXOPT_MISSING_OK;
-           (void) ReindexRelationConcurrently(relid, &newparams);
+           (void) ReindexRelationConcurrently(stmt, relid, &newparams);
            /* ReindexRelationConcurrently() does the verbose output */
        }
        else if (relkind == RELKIND_INDEX)
@@ -3350,7 +3355,7 @@ ReindexMultipleInternal(const List *relids, const ReindexParams *params)
 
            newparams.options |=
                REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
-           reindex_index(relid, false, relpersistence, &newparams);
+           reindex_index(stmt, relid, false, relpersistence, &newparams);
            PopActiveSnapshot();
            /* reindex_index() does the verbose output */
        }
@@ -3361,7 +3366,7 @@ ReindexMultipleInternal(const List *relids, const ReindexParams *params)
 
            newparams.options |=
                REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
-           result = reindex_relation(relid,
+           result = reindex_relation(stmt, relid,
                                      REINDEX_REL_PROCESS_TOAST |
                                      REINDEX_REL_CHECK_CONSTRAINTS,
                                      &newparams);
@@ -3406,7 +3411,7 @@ ReindexMultipleInternal(const List *relids, const ReindexParams *params)
  * anyway, and a non-concurrent reindex is more efficient.
  */
 static bool
-ReindexRelationConcurrently(Oid relationOid, const ReindexParams *params)
+ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const ReindexParams *params)
 {
    typedef struct ReindexIndexInfo
    {
@@ -3843,6 +3848,20 @@ ReindexRelationConcurrently(Oid relationOid, const ReindexParams *params)
        SetUserIdAndSecContext(save_userid, save_sec_context);
 
        table_close(heapRel, NoLock);
+
+       /*
+        * If a statement is available, telling that this comes from a REINDEX
+        * command, collect the new index for event triggers.
+        */
+       if (stmt)
+       {
+           ObjectAddress address;
+
+           ObjectAddressSet(address, RelationRelationId, newIndexId);
+           EventTriggerCollectSimpleCommand(address,
+                                            InvalidObjectAddress,
+                                            (Node *) stmt);
+       }
    }
 
    /*
index 7206da7c53c06490c8eea369da3a65fdf045ba07..6b0a20010e239054e57dd016068c6d471086c13b 100644 (file)
@@ -2169,7 +2169,7 @@ ExecuteTruncateGuts(List *explicit_rels,
            /*
             * Reconstruct the indexes to match, and we're done.
             */
-           reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
+           reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
                             &reindex_params);
        }
 
index e3ccf6c7f7e8b03718d395b5fc97b29420452412..366a27ae8ed578a3d22094df3f1b0515258082b6 100644 (file)
@@ -960,10 +960,6 @@ standard_ProcessUtility(PlannedStmt *pstmt,
                              (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
            break;
 
-       case T_ReindexStmt:
-           ExecReindex(pstate, (ReindexStmt *) parsetree, isTopLevel);
-           break;
-
            /*
             * The following statements are supported by Event Triggers only
             * in some cases, so we "fast path" them in the other cases.
@@ -1574,6 +1570,13 @@ ProcessUtilitySlow(ParseState *pstate,
                }
                break;
 
+           case T_ReindexStmt:
+               ExecReindex(pstate, (ReindexStmt *) parsetree, isTopLevel);
+
+               /* EventTriggerCollectSimpleCommand is called directly */
+               commandCollected = true;
+               break;
+
            case T_CreateExtensionStmt:
                address = CreateExtension(pstate, (CreateExtensionStmt *) parsetree);
                break;
index a4770eaf120f947394a475d1b228b26240460c44..41a0b9aee1e6951daa37abdd674a10d6a0a2cf3b 100644 (file)
@@ -149,8 +149,9 @@ extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action);
 
 extern Oid IndexGetRelation(Oid indexId, bool missing_ok);
 
-extern void reindex_index(Oid indexId, bool skip_constraint_checks,
-                         char persistence, const ReindexParams *params);
+extern void reindex_index(const ReindexStmt *stmt, Oid indexId,
+                         bool skip_constraint_checks, char persistence,
+                         const ReindexParams *params);
 
 /* Flag bits for reindex_relation(): */
 #define REINDEX_REL_PROCESS_TOAST          0x01
@@ -159,7 +160,8 @@ extern void reindex_index(Oid indexId, bool skip_constraint_checks,
 #define REINDEX_REL_FORCE_INDEXES_UNLOGGED 0x08
 #define REINDEX_REL_FORCE_INDEXES_PERMANENT 0x10
 
-extern bool reindex_relation(Oid relid, int flags, const ReindexParams *params);
+extern bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags,
+                            const ReindexParams *params);
 
 extern bool ReindexIsProcessingHeap(Oid heapOid);
 extern bool ReindexIsProcessingIndex(Oid indexOid);
index 553a31874f134d6baf3b70550041eef0977af904..320ee91512882bc3832f03b3271ad3b2daab5856 100644 (file)
@@ -194,7 +194,7 @@ PG_CMDTAG(CMDTAG_PREPARE, "PREPARE", false, false, false)
 PG_CMDTAG(CMDTAG_PREPARE_TRANSACTION, "PREPARE TRANSACTION", false, false, false)
 PG_CMDTAG(CMDTAG_REASSIGN_OWNED, "REASSIGN OWNED", false, false, false)
 PG_CMDTAG(CMDTAG_REFRESH_MATERIALIZED_VIEW, "REFRESH MATERIALIZED VIEW", true, false, false)
-PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", false, false, false)
+PG_CMDTAG(CMDTAG_REINDEX, "REINDEX", true, false, false)
 PG_CMDTAG(CMDTAG_RELEASE, "RELEASE", false, false, false)
 PG_CMDTAG(CMDTAG_RESET, "RESET", false, false, false)
 PG_CMDTAG(CMDTAG_REVOKE, "REVOKE", true, false, false)
index 0b87a42d0a9ac6ba17fd86ad926fc33001a22cc7..fdcd1279459fd6c0a00167e5894d7caf916292b3 100644 (file)
@@ -556,6 +556,58 @@ ERROR:  cannot alter type "rewritetype" because column "rewritemetoo3.a" uses it
 drop table rewriteme;
 drop event trigger no_rewrite_allowed;
 drop function test_evtrig_no_rewrite();
+-- Tests for REINDEX
+CREATE OR REPLACE FUNCTION reindex_start_command()
+RETURNS event_trigger AS $$
+BEGIN
+    RAISE NOTICE 'REINDEX START: % %', tg_event, tg_tag;
+END;
+$$ LANGUAGE plpgsql;
+CREATE EVENT TRIGGER regress_reindex_start ON ddl_command_start
+    WHEN TAG IN ('REINDEX')
+    EXECUTE PROCEDURE reindex_start_command();
+CREATE FUNCTION reindex_end_command()
+RETURNS event_trigger AS $$
+DECLARE
+    obj record;
+BEGIN
+    FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
+    LOOP
+        RAISE NOTICE 'REINDEX END: command_tag=% type=% identity=%',
+       obj.command_tag, obj.object_type, obj.object_identity;
+    END LOOP;
+END;
+$$ LANGUAGE plpgsql;
+CREATE EVENT TRIGGER regress_reindex_end ON ddl_command_end
+    WHEN TAG IN ('REINDEX')
+    EXECUTE PROCEDURE reindex_end_command();
+CREATE TABLE concur_reindex_tab (c1 int);
+CREATE INDEX concur_reindex_ind ON concur_reindex_tab (c1);
+-- Both start and end triggers enabled.
+REINDEX INDEX concur_reindex_ind;
+NOTICE:  REINDEX START: ddl_command_start REINDEX
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+REINDEX TABLE concur_reindex_tab;
+NOTICE:  REINDEX START: ddl_command_start REINDEX
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+REINDEX INDEX CONCURRENTLY concur_reindex_ind;
+NOTICE:  REINDEX START: ddl_command_start REINDEX
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+NOTICE:  REINDEX START: ddl_command_start REINDEX
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+-- with start trigger disabled.
+ALTER EVENT TRIGGER regress_reindex_start DISABLE;
+REINDEX INDEX concur_reindex_ind;
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+REINDEX INDEX CONCURRENTLY concur_reindex_ind;
+NOTICE:  REINDEX END: command_tag=REINDEX type=index identity=public.concur_reindex_ind
+-- Clean up
+DROP EVENT TRIGGER regress_reindex_start;
+DROP EVENT TRIGGER regress_reindex_end;
+DROP FUNCTION reindex_end_command();
+DROP FUNCTION reindex_start_command();
+DROP TABLE concur_reindex_tab;
 -- test Row Security Event Trigger
 RESET SESSION AUTHORIZATION;
 CREATE TABLE event_trigger_test (a integer, b text);
index 6f0933b9e88aa53ef0d6eb10a1b0a8a4c5b03ba6..e5c8bf841226d46de22d2e261e76a9239469057f 100644 (file)
@@ -418,6 +418,51 @@ drop table rewriteme;
 drop event trigger no_rewrite_allowed;
 drop function test_evtrig_no_rewrite();
 
+-- Tests for REINDEX
+CREATE OR REPLACE FUNCTION reindex_start_command()
+RETURNS event_trigger AS $$
+BEGIN
+    RAISE NOTICE 'REINDEX START: % %', tg_event, tg_tag;
+END;
+$$ LANGUAGE plpgsql;
+CREATE EVENT TRIGGER regress_reindex_start ON ddl_command_start
+    WHEN TAG IN ('REINDEX')
+    EXECUTE PROCEDURE reindex_start_command();
+CREATE FUNCTION reindex_end_command()
+RETURNS event_trigger AS $$
+DECLARE
+    obj record;
+BEGIN
+    FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
+    LOOP
+        RAISE NOTICE 'REINDEX END: command_tag=% type=% identity=%',
+       obj.command_tag, obj.object_type, obj.object_identity;
+    END LOOP;
+END;
+$$ LANGUAGE plpgsql;
+CREATE EVENT TRIGGER regress_reindex_end ON ddl_command_end
+    WHEN TAG IN ('REINDEX')
+    EXECUTE PROCEDURE reindex_end_command();
+
+CREATE TABLE concur_reindex_tab (c1 int);
+CREATE INDEX concur_reindex_ind ON concur_reindex_tab (c1);
+-- Both start and end triggers enabled.
+REINDEX INDEX concur_reindex_ind;
+REINDEX TABLE concur_reindex_tab;
+REINDEX INDEX CONCURRENTLY concur_reindex_ind;
+REINDEX TABLE CONCURRENTLY concur_reindex_tab;
+-- with start trigger disabled.
+ALTER EVENT TRIGGER regress_reindex_start DISABLE;
+REINDEX INDEX concur_reindex_ind;
+REINDEX INDEX CONCURRENTLY concur_reindex_ind;
+
+-- Clean up
+DROP EVENT TRIGGER regress_reindex_start;
+DROP EVENT TRIGGER regress_reindex_end;
+DROP FUNCTION reindex_end_command();
+DROP FUNCTION reindex_start_command();
+DROP TABLE concur_reindex_tab;
+
 -- test Row Security Event Trigger
 RESET SESSION AUTHORIZATION;
 CREATE TABLE event_trigger_test (a integer, b text);