@@ -2806,6 +2806,9 @@ index_update_stats(Relation rel,
2806
2806
bool hasindex ,
2807
2807
double reltuples )
2808
2808
{
2809
+ bool update_stats ;
2810
+ BlockNumber relpages ;
2811
+ BlockNumber relallvisible ;
2809
2812
Oid relid = RelationGetRelid (rel );
2810
2813
Relation pg_class ;
2811
2814
ScanKeyData key [1 ];
@@ -2814,6 +2817,42 @@ index_update_stats(Relation rel,
2814
2817
Form_pg_class rd_rel ;
2815
2818
bool dirty ;
2816
2819
2820
+ /*
2821
+ * As a special hack, if we are dealing with an empty table and the
2822
+ * existing reltuples is -1, we leave that alone. This ensures that
2823
+ * creating an index as part of CREATE TABLE doesn't cause the table to
2824
+ * prematurely look like it's been vacuumed. The rd_rel we modify may
2825
+ * differ from rel->rd_rel due to e.g. commit of concurrent GRANT, but the
2826
+ * commands that change reltuples take locks conflicting with ours. (Even
2827
+ * if a command changed reltuples under a weaker lock, this affects only
2828
+ * statistics for an empty table.)
2829
+ */
2830
+ if (reltuples == 0 && rel -> rd_rel -> reltuples < 0 )
2831
+ reltuples = -1 ;
2832
+
2833
+ /*
2834
+ * Don't update statistics during binary upgrade, because the indexes are
2835
+ * created before the data is moved into place.
2836
+ */
2837
+ update_stats = reltuples >=0 && !IsBinaryUpgrade ;
2838
+
2839
+ /*
2840
+ * Finish I/O and visibility map buffer locks before
2841
+ * systable_inplace_update_begin() locks the pg_class buffer. The rd_rel
2842
+ * we modify may differ from rel->rd_rel due to e.g. commit of concurrent
2843
+ * GRANT, but no command changes a relkind from non-index to index. (Even
2844
+ * if one did, relallvisible doesn't break functionality.)
2845
+ */
2846
+ if (update_stats )
2847
+ {
2848
+ relpages = RelationGetNumberOfBlocks (rel );
2849
+
2850
+ if (rel -> rd_rel -> relkind != RELKIND_INDEX )
2851
+ visibilitymap_count (rel ,& relallvisible ,NULL );
2852
+ else /* don't bother for indexes */
2853
+ relallvisible = 0 ;
2854
+ }
2855
+
2817
2856
/*
2818
2857
* We always update the pg_class row using a non-transactional,
2819
2858
* overwrite-in-place update. There are several reasons for this:
@@ -2858,15 +2897,6 @@ index_update_stats(Relation rel,
2858
2897
/* Should this be a more comprehensive test? */
2859
2898
Assert (rd_rel -> relkind != RELKIND_PARTITIONED_INDEX );
2860
2899
2861
- /*
2862
- * As a special hack, if we are dealing with an empty table and the
2863
- * existing reltuples is -1, we leave that alone. This ensures that
2864
- * creating an index as part of CREATE TABLE doesn't cause the table to
2865
- * prematurely look like it's been vacuumed.
2866
- */
2867
- if (reltuples == 0 && rd_rel -> reltuples < 0 )
2868
- reltuples = -1 ;
2869
-
2870
2900
/* Apply required updates, if any, to copied tuple */
2871
2901
2872
2902
dirty = false;
@@ -2876,20 +2906,8 @@ index_update_stats(Relation rel,
2876
2906
dirty = true;
2877
2907
}
2878
2908
2879
- /*
2880
- * Avoid updating statistics during binary upgrade, because the indexes
2881
- * are created before the data is moved into place.
2882
- */
2883
- if (reltuples >=0 && !IsBinaryUpgrade )
2909
+ if (update_stats )
2884
2910
{
2885
- BlockNumber relpages = RelationGetNumberOfBlocks (rel );
2886
- BlockNumber relallvisible ;
2887
-
2888
- if (rd_rel -> relkind != RELKIND_INDEX )
2889
- visibilitymap_count (rel ,& relallvisible ,NULL );
2890
- else /* don't bother for indexes */
2891
- relallvisible = 0 ;
2892
-
2893
2911
if (rd_rel -> relpages != (int32 )relpages )
2894
2912
{
2895
2913
rd_rel -> relpages = (int32 )relpages ;