@@ -47,6 +47,7 @@ typedef struct vacuumingOptions
47
47
bool process_toast ;
48
48
bool skip_database_stats ;
49
49
char * buffer_usage_limit ;
50
+ bool missing_only ;
50
51
} vacuumingOptions ;
51
52
52
53
/* object filter options */
@@ -128,6 +129,7 @@ main(int argc, char *argv[])
128
129
{"no-process-toast" , no_argument , NULL , 11 },
129
130
{"no-process-main" , no_argument , NULL , 12 },
130
131
{"buffer-usage-limit" , required_argument , NULL , 13 },
132
+ {"missing-only" , no_argument , NULL , 14 },
131
133
{NULL , 0 , NULL , 0 }
132
134
};
133
135
@@ -275,6 +277,9 @@ main(int argc, char *argv[])
275
277
case 13 :
276
278
vacopts .buffer_usage_limit = escape_quotes (optarg );
277
279
break ;
280
+ case 14 :
281
+ vacopts .missing_only = true;
282
+ break ;
278
283
default :
279
284
/* getopt_long already emitted a complaint */
280
285
pg_log_error_hint ("Try \"%s --help\" for more information." , progname );
@@ -360,6 +365,11 @@ main(int argc, char *argv[])
360
365
pg_fatal ("cannot use the \"%s\" option with the \"%s\" option" ,
361
366
"buffer-usage-limit" , "full" );
362
367
368
+ /* Prohibit --missing-only without --analyze-only or --analyze-in-stages */
369
+ if (vacopts .missing_only && !vacopts .analyze_only )
370
+ pg_fatal ("cannot use the \"%s\" option without \"%s\" or \"%s\"" ,
371
+ "missing-only" , "analyze-only" , "analyze-in-stages" );
372
+
363
373
/* fill cparams except for dbname, which is set below */
364
374
cparams .pghost = host ;
365
375
cparams .pgport = port ;
@@ -584,6 +594,13 @@ vacuum_one_database(ConnParams *cparams,
584
594
"--buffer-usage-limit" , "16" );
585
595
}
586
596
597
+ if (vacopts -> missing_only && PQserverVersion (conn ) < 150000 )
598
+ {
599
+ PQfinish (conn );
600
+ pg_fatal ("cannot use the \"%s\" option on server versions older than PostgreSQL %s" ,
601
+ "--missing-only" , "15" );
602
+ }
603
+
587
604
/* skip_database_stats is used automatically if server supports it */
588
605
vacopts -> skip_database_stats = (PQserverVersion (conn ) >= 160000 );
589
606
@@ -672,6 +689,7 @@ vacuum_one_database(ConnParams *cparams,
672
689
" FROM pg_catalog.pg_class c\n"
673
690
" JOIN pg_catalog.pg_namespace ns"
674
691
" ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
692
+ " CROSS JOIN LATERAL (SELECT c.relkind IN ('p', 'I')) as p (inherited)\n"
675
693
" LEFT JOIN pg_catalog.pg_class t"
676
694
" ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n" );
677
695
@@ -755,6 +773,79 @@ vacuum_one_database(ConnParams *cparams,
755
773
vacopts -> min_mxid_age );
756
774
}
757
775
776
+ if (vacopts -> missing_only )
777
+ {
778
+ appendPQExpBufferStr (& catalog_query , " AND (\n" );
779
+
780
+ /* regular stats */
781
+ appendPQExpBufferStr (& catalog_query ,
782
+ " EXISTS (SELECT NULL FROM pg_catalog.pg_attribute a\n"
783
+ " WHERE a.attrelid OPERATOR(pg_catalog.=) c.oid\n"
784
+ " AND c.reltuples OPERATOR(pg_catalog.!=) 0::pg_catalog.float4\n"
785
+ " AND a.attnum OPERATOR(pg_catalog.>) 0::pg_catalog.int2\n"
786
+ " AND NOT a.attisdropped\n"
787
+ " AND a.attstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
788
+ " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
789
+ " WHERE s.starelid OPERATOR(pg_catalog.=) a.attrelid\n"
790
+ " AND s.staattnum OPERATOR(pg_catalog.=) a.attnum\n"
791
+ " AND s.stainherit OPERATOR(pg_catalog.=) p.inherited))\n" );
792
+
793
+ /* extended stats */
794
+ appendPQExpBufferStr (& catalog_query ,
795
+ " OR EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext e\n"
796
+ " WHERE e.stxrelid OPERATOR(pg_catalog.=) c.oid\n"
797
+ " AND c.reltuples OPERATOR(pg_catalog.!=) 0::pg_catalog.float4\n"
798
+ " AND e.stxstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
799
+ " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext_data d\n"
800
+ " WHERE d.stxoid OPERATOR(pg_catalog.=) e.oid\n"
801
+ " AND d.stxdinherit OPERATOR(pg_catalog.=) p.inherited))\n" );
802
+
803
+ /* expression indexes */
804
+ appendPQExpBufferStr (& catalog_query ,
805
+ " OR EXISTS (SELECT NULL FROM pg_catalog.pg_index i\n"
806
+ " CROSS JOIN LATERAL pg_catalog.unnest(i.indkey) WITH ORDINALITY u (attnum, ord)\n"
807
+ " WHERE i.indrelid OPERATOR(pg_catalog.=) c.oid\n"
808
+ " AND c.reltuples OPERATOR(pg_catalog.!=) 0::pg_catalog.float4\n"
809
+ " AND i.indexprs IS NOT NULL\n"
810
+ " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
811
+ " WHERE s.starelid OPERATOR(pg_catalog.=) i.indexrelid\n"
812
+ " AND s.staattnum OPERATOR(pg_catalog.=) u.ord\n"
813
+ " AND s.stainherit OPERATOR(pg_catalog.=) p.inherited))\n" );
814
+
815
+ /* table inheritance and regular stats */
816
+ appendPQExpBufferStr (& catalog_query ,
817
+ " OR EXISTS (SELECT NULL FROM pg_catalog.pg_attribute a\n"
818
+ " WHERE a.attrelid OPERATOR(pg_catalog.=) c.oid\n"
819
+ " AND c.reltuples OPERATOR(pg_catalog.!=) 0::pg_catalog.float4\n"
820
+ " AND a.attnum OPERATOR(pg_catalog.>) 0::pg_catalog.int2\n"
821
+ " AND NOT a.attisdropped\n"
822
+ " AND a.attstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
823
+ " AND c.relhassubclass\n"
824
+ " AND NOT p.inherited\n"
825
+ " AND EXISTS (SELECT NULL FROM pg_catalog.pg_inherits h\n"
826
+ " WHERE h.inhparent OPERATOR(pg_catalog.=) c.oid)\n"
827
+ " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
828
+ " WHERE s.starelid OPERATOR(pg_catalog.=) a.attrelid\n"
829
+ " AND s.staattnum OPERATOR(pg_catalog.=) a.attnum\n"
830
+ " AND s.stainherit))\n" );
831
+
832
+ /* table inheritance and extended stats */
833
+ appendPQExpBufferStr (& catalog_query ,
834
+ " OR EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext e\n"
835
+ " WHERE e.stxrelid OPERATOR(pg_catalog.=) c.oid\n"
836
+ " AND c.reltuples OPERATOR(pg_catalog.!=) 0::pg_catalog.float4\n"
837
+ " AND e.stxstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
838
+ " AND c.relhassubclass\n"
839
+ " AND NOT p.inherited\n"
840
+ " AND EXISTS (SELECT NULL FROM pg_catalog.pg_inherits h\n"
841
+ " WHERE h.inhparent OPERATOR(pg_catalog.=) c.oid)\n"
842
+ " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext_data d\n"
843
+ " WHERE d.stxoid OPERATOR(pg_catalog.=) e.oid\n"
844
+ " AND d.stxdinherit))\n" );
845
+
846
+ appendPQExpBufferStr (& catalog_query , " )\n" );
847
+ }
848
+
758
849
/*
759
850
* Execute the catalog query. We use the default search_path for this
760
851
* query for consistency with table lookups done elsewhere by the user.
@@ -1181,6 +1272,7 @@ help(const char *progname)
1181
1272
printf (_ (" -j, --jobs=NUM use this many concurrent connections to vacuum\n" ));
1182
1273
printf (_ (" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n" ));
1183
1274
printf (_ (" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n" ));
1275
+ printf (_ (" --missing-only only analyze relations with missing statistics\n" ));
1184
1276
printf (_ (" --no-index-cleanup don't remove index entries that point to dead tuples\n" ));
1185
1277
printf (_ (" --no-process-main skip the main relation\n" ));
1186
1278
printf (_ (" --no-process-toast skip the TOAST table associated with the table to vacuum\n" ));
0 commit comments