@@ -2812,6 +2812,9 @@ index_update_stats(Relation rel,
28122812bool hasindex ,
28132813double reltuples )
28142814{
2815+ bool update_stats ;
2816+ BlockNumber relpages ;
2817+ BlockNumber relallvisible ;
28152818Oid relid = RelationGetRelid (rel );
28162819Relation pg_class ;
28172820ScanKeyData key [1 ];
@@ -2820,6 +2823,38 @@ index_update_stats(Relation rel,
28202823Form_pg_class rd_rel ;
28212824bool dirty ;
28222825
2826+ /*
2827+ * As a special hack, if we are dealing with an empty table and the
2828+ * existing reltuples is -1, we leave that alone. This ensures that
2829+ * creating an index as part of CREATE TABLE doesn't cause the table to
2830+ * prematurely look like it's been vacuumed. The rd_rel we modify may
2831+ * differ from rel->rd_rel due to e.g. commit of concurrent GRANT, but the
2832+ * commands that change reltuples take locks conflicting with ours. (Even
2833+ * if a command changed reltuples under a weaker lock, this affects only
2834+ * statistics for an empty table.)
2835+ */
2836+ if (reltuples == 0 && rel -> rd_rel -> reltuples < 0 )
2837+ reltuples = -1 ;
2838+
2839+ update_stats = reltuples >=0 ;
2840+
2841+ /*
2842+ * Finish I/O and visibility map buffer locks before
2843+ * systable_inplace_update_begin() locks the pg_class buffer. The rd_rel
2844+ * we modify may differ from rel->rd_rel due to e.g. commit of concurrent
2845+ * GRANT, but no command changes a relkind from non-index to index. (Even
2846+ * if one did, relallvisible doesn't break functionality.)
2847+ */
2848+ if (update_stats )
2849+ {
2850+ relpages = RelationGetNumberOfBlocks (rel );
2851+
2852+ if (rel -> rd_rel -> relkind != RELKIND_INDEX )
2853+ visibilitymap_count (rel ,& relallvisible ,NULL );
2854+ else /* don't bother for indexes */
2855+ relallvisible = 0 ;
2856+ }
2857+
28232858/*
28242859 * We always update the pg_class row using a non-transactional,
28252860 * overwrite-in-place update. There are several reasons for this:
@@ -2864,15 +2899,6 @@ index_update_stats(Relation rel,
28642899/* Should this be a more comprehensive test? */
28652900Assert (rd_rel -> relkind != RELKIND_PARTITIONED_INDEX );
28662901
2867- /*
2868- * As a special hack, if we are dealing with an empty table and the
2869- * existing reltuples is -1, we leave that alone. This ensures that
2870- * creating an index as part of CREATE TABLE doesn't cause the table to
2871- * prematurely look like it's been vacuumed.
2872- */
2873- if (reltuples == 0 && rd_rel -> reltuples < 0 )
2874- reltuples = -1 ;
2875-
28762902/* Apply required updates, if any, to copied tuple */
28772903
28782904dirty = false;
@@ -2882,16 +2908,8 @@ index_update_stats(Relation rel,
28822908dirty = true;
28832909}
28842910
2885- if (reltuples >= 0 )
2911+ if (update_stats )
28862912{
2887- BlockNumber relpages = RelationGetNumberOfBlocks (rel );
2888- BlockNumber relallvisible ;
2889-
2890- if (rd_rel -> relkind != RELKIND_INDEX )
2891- visibilitymap_count (rel ,& relallvisible ,NULL );
2892- else /* don't bother for indexes */
2893- relallvisible = 0 ;
2894-
28952913if (rd_rel -> relpages != (int32 )relpages )
28962914{
28972915rd_rel -> relpages = (int32 )relpages ;