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