PostgreSQL Source Code git master
vacuuming.c File Reference
#include "postgres_fe.h"
#include "catalog/pg_attribute_d.h"
#include "catalog/pg_class_d.h"
#include "common/connect.h"
#include "common/logging.h"
#include "fe_utils/cancel.h"
#include "fe_utils/option_utils.h"
#include "fe_utils/parallel_slot.h"
#include "fe_utils/query_utils.h"
#include "fe_utils/string_utils.h"
#include "vacuuming.h"
Include dependency graph for vacuuming.c:

Go to the source code of this file.

Functions

static int vacuum_one_database (ConnParams *cparams, vacuumingOptions *vacopts, int stage, SimpleStringList *objects, SimpleStringList **found_objs, int concurrentCons, const char *progname, bool echo, bool quiet)
 
static int vacuum_all_databases (ConnParams *cparams, vacuumingOptions *vacopts, SimpleStringList *objects, int concurrentCons, const char *progname, bool echo, bool quiet)
 
static SimpleStringListretrieve_objects (PGconn *conn, vacuumingOptions *vacopts, SimpleStringList *objects, bool echo)
 
static void free_retrieved_objects (SimpleStringList *list)
 
static void prepare_vacuum_command (PGconn *conn, PQExpBuffer sql, vacuumingOptions *vacopts, const char *table)
 
static void run_vacuum_command (PGconn *conn, const char *sql, bool echo, const char *table)
 
int vacuuming_main (ConnParams *cparams, const char *dbname, const char *maintenance_db, vacuumingOptions *vacopts, SimpleStringList *objects, unsigned int tbl_count, int concurrentCons, const char *progname, bool echo, bool quiet)
 
char * escape_quotes (const char *src)
 

Function Documentation

◆ escape_quotes()

static char * escape_quotes ( const char *  src)

Definition at line 1034 of file vacuuming.c.

1035{
1036 char *result = escape_single_quotes_ascii(src);
1037
1038 if (!result)
1039 pg_fatal("out of memory");
1040 return result;
1041}
#define pg_fatal(...)
char * escape_single_quotes_ascii(const char *src)
Definition: quotes.c:33

References escape_single_quotes_ascii(), and pg_fatal.

◆ free_retrieved_objects()

static void free_retrieved_objects ( SimpleStringList list)
static

Definition at line 815 of file vacuuming.c.

816{
817 if (list)
818 {
820 pg_free(list);
821 }
822}
void pg_free(void *ptr)
Definition: fe_memutils.c:105
void simple_string_list_destroy(SimpleStringList *list)
Definition: simple_list.c:125

References sort-test::list, pg_free(), and simple_string_list_destroy().

Referenced by vacuum_all_databases(), vacuum_one_database(), and vacuuming_main().

◆ prepare_vacuum_command()

static void prepare_vacuum_command ( PGconn conn,
PQExpBuffer  sql,
vacuumingOptions vacopts,
const char *  table 
)
static

Definition at line 832 of file vacuuming.c.

834{
835 int serverVersion = PQserverVersion(conn);
836 const char *paren = " (";
837 const char *comma = ", ";
838 const char *sep = paren;
839
840 resetPQExpBuffer(sql);
841
842 if (vacopts->mode == MODE_ANALYZE ||
843 vacopts->mode == MODE_ANALYZE_IN_STAGES)
844 {
845 appendPQExpBufferStr(sql, "ANALYZE");
846
847 /* parenthesized grammar of ANALYZE is supported since v11 */
848 if (serverVersion >= 110000)
849 {
850 if (vacopts->skip_locked)
851 {
852 /* SKIP_LOCKED is supported since v12 */
853 Assert(serverVersion >= 120000);
854 appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
855 sep = comma;
856 }
857 if (vacopts->verbose)
858 {
859 appendPQExpBuffer(sql, "%sVERBOSE", sep);
860 sep = comma;
861 }
862 if (vacopts->buffer_usage_limit)
863 {
864 Assert(serverVersion >= 160000);
865 appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep,
866 vacopts->buffer_usage_limit);
867 sep = comma;
868 }
869 if (sep != paren)
870 appendPQExpBufferChar(sql, ')');
871 }
872 else
873 {
874 if (vacopts->verbose)
875 appendPQExpBufferStr(sql, " VERBOSE");
876 }
877 }
878 else
879 {
880 appendPQExpBufferStr(sql, "VACUUM");
881
882 /* parenthesized grammar of VACUUM is supported since v9.0 */
883 if (serverVersion >= 90000)
884 {
885 if (vacopts->disable_page_skipping)
886 {
887 /* DISABLE_PAGE_SKIPPING is supported since v9.6 */
888 Assert(serverVersion >= 90600);
889 appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep);
890 sep = comma;
891 }
892 if (vacopts->no_index_cleanup)
893 {
894 /* "INDEX_CLEANUP FALSE" has been supported since v12 */
895 Assert(serverVersion >= 120000);
896 Assert(!vacopts->force_index_cleanup);
897 appendPQExpBuffer(sql, "%sINDEX_CLEANUP FALSE", sep);
898 sep = comma;
899 }
900 if (vacopts->force_index_cleanup)
901 {
902 /* "INDEX_CLEANUP TRUE" has been supported since v12 */
903 Assert(serverVersion >= 120000);
904 Assert(!vacopts->no_index_cleanup);
905 appendPQExpBuffer(sql, "%sINDEX_CLEANUP TRUE", sep);
906 sep = comma;
907 }
908 if (!vacopts->do_truncate)
909 {
910 /* TRUNCATE is supported since v12 */
911 Assert(serverVersion >= 120000);
912 appendPQExpBuffer(sql, "%sTRUNCATE FALSE", sep);
913 sep = comma;
914 }
915 if (!vacopts->process_main)
916 {
917 /* PROCESS_MAIN is supported since v16 */
918 Assert(serverVersion >= 160000);
919 appendPQExpBuffer(sql, "%sPROCESS_MAIN FALSE", sep);
920 sep = comma;
921 }
922 if (!vacopts->process_toast)
923 {
924 /* PROCESS_TOAST is supported since v14 */
925 Assert(serverVersion >= 140000);
926 appendPQExpBuffer(sql, "%sPROCESS_TOAST FALSE", sep);
927 sep = comma;
928 }
929 if (vacopts->skip_database_stats)
930 {
931 /* SKIP_DATABASE_STATS is supported since v16 */
932 Assert(serverVersion >= 160000);
933 appendPQExpBuffer(sql, "%sSKIP_DATABASE_STATS", sep);
934 sep = comma;
935 }
936 if (vacopts->skip_locked)
937 {
938 /* SKIP_LOCKED is supported since v12 */
939 Assert(serverVersion >= 120000);
940 appendPQExpBuffer(sql, "%sSKIP_LOCKED", sep);
941 sep = comma;
942 }
943 if (vacopts->full)
944 {
945 appendPQExpBuffer(sql, "%sFULL", sep);
946 sep = comma;
947 }
948 if (vacopts->freeze)
949 {
950 appendPQExpBuffer(sql, "%sFREEZE", sep);
951 sep = comma;
952 }
953 if (vacopts->verbose)
954 {
955 appendPQExpBuffer(sql, "%sVERBOSE", sep);
956 sep = comma;
957 }
958 if (vacopts->and_analyze)
959 {
960 appendPQExpBuffer(sql, "%sANALYZE", sep);
961 sep = comma;
962 }
963 if (vacopts->parallel_workers >= 0)
964 {
965 /* PARALLEL is supported since v13 */
966 Assert(serverVersion >= 130000);
967 appendPQExpBuffer(sql, "%sPARALLEL %d", sep,
968 vacopts->parallel_workers);
969 sep = comma;
970 }
971 if (vacopts->buffer_usage_limit)
972 {
973 Assert(serverVersion >= 160000);
974 appendPQExpBuffer(sql, "%sBUFFER_USAGE_LIMIT '%s'", sep,
975 vacopts->buffer_usage_limit);
976 sep = comma;
977 }
978 if (sep != paren)
979 appendPQExpBufferChar(sql, ')');
980 }
981 else
982 {
983 if (vacopts->full)
984 appendPQExpBufferStr(sql, " FULL");
985 if (vacopts->freeze)
986 appendPQExpBufferStr(sql, " FREEZE");
987 if (vacopts->verbose)
988 appendPQExpBufferStr(sql, " VERBOSE");
989 if (vacopts->and_analyze)
990 appendPQExpBufferStr(sql, " ANALYZE");
991 }
992 }
993
994 appendPQExpBuffer(sql, " %s;", table);
995}
int PQserverVersion(const PGconn *conn)
Definition: fe-connect.c:7694
Assert(PointerIsAligned(start, uint64))
#define comma
Definition: indent_codes.h:48
static const struct lconv_member_info table[]
void resetPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:146
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
PGconn * conn
Definition: streamutil.c:52
bool force_index_cleanup
Definition: vacuuming.h:47
RunMode mode
Definition: vacuuming.h:34
bool no_index_cleanup
Definition: vacuuming.h:46
int parallel_workers
Definition: vacuuming.h:44
bool disable_page_skipping
Definition: vacuuming.h:40
bool process_toast
Definition: vacuuming.h:50
char * buffer_usage_limit
Definition: vacuuming.h:52
bool skip_database_stats
Definition: vacuuming.h:51
@ MODE_ANALYZE
Definition: vacuuming.h:23
@ MODE_ANALYZE_IN_STAGES
Definition: vacuuming.h:24

References vacuumingOptions::and_analyze, appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert(), vacuumingOptions::buffer_usage_limit, comma, conn, vacuumingOptions::disable_page_skipping, vacuumingOptions::do_truncate, vacuumingOptions::force_index_cleanup, vacuumingOptions::freeze, vacuumingOptions::full, vacuumingOptions::mode, MODE_ANALYZE, MODE_ANALYZE_IN_STAGES, vacuumingOptions::no_index_cleanup, vacuumingOptions::parallel_workers, PQserverVersion(), vacuumingOptions::process_main, vacuumingOptions::process_toast, resetPQExpBuffer(), vacuumingOptions::skip_database_stats, vacuumingOptions::skip_locked, table, and vacuumingOptions::verbose.

Referenced by vacuum_one_database().

◆ retrieve_objects()

static SimpleStringList * retrieve_objects ( PGconn conn,
vacuumingOptions vacopts,
SimpleStringList objects,
bool  echo 
)
static

Definition at line 526 of file vacuuming.c.

528{
530 PQExpBufferData catalog_query;
531 PGresult *res;
533 SimpleStringList *found_objs = palloc0(sizeof(SimpleStringList));
534 bool objects_listed = false;
535
536 initPQExpBuffer(&catalog_query);
537 for (cell = objects ? objects->head : NULL; cell; cell = cell->next)
538 {
539 char *just_table = NULL;
540 const char *just_columns = NULL;
541
542 if (!objects_listed)
543 {
544 appendPQExpBufferStr(&catalog_query,
545 "WITH listed_objects (object_oid, column_list) AS (\n"
546 " VALUES (");
547 objects_listed = true;
548 }
549 else
550 appendPQExpBufferStr(&catalog_query, ",\n (");
551
553 {
554 appendStringLiteralConn(&catalog_query, cell->val, conn);
555 appendPQExpBufferStr(&catalog_query, "::pg_catalog.regnamespace, ");
556 }
557
558 if (vacopts->objfilter & OBJFILTER_TABLE)
559 {
560 /*
561 * Split relation and column names given by the user, this is used
562 * to feed the CTE with values on which are performed pre-run
563 * validity checks as well. For now these happen only on the
564 * relation name.
565 */
567 &just_table, &just_columns);
568
569 appendStringLiteralConn(&catalog_query, just_table, conn);
570 appendPQExpBufferStr(&catalog_query, "::pg_catalog.regclass, ");
571 }
572
573 if (just_columns && just_columns[0] != '\0')
574 appendStringLiteralConn(&catalog_query, just_columns, conn);
575 else
576 appendPQExpBufferStr(&catalog_query, "NULL");
577
578 appendPQExpBufferStr(&catalog_query, "::pg_catalog.text)");
579
580 pg_free(just_table);
581 }
582
583 /* Finish formatting the CTE */
584 if (objects_listed)
585 appendPQExpBufferStr(&catalog_query, "\n)\n");
586
587 appendPQExpBufferStr(&catalog_query, "SELECT c.relname, ns.nspname");
588
589 if (objects_listed)
590 appendPQExpBufferStr(&catalog_query, ", listed_objects.column_list");
591
592 appendPQExpBufferStr(&catalog_query,
593 " FROM pg_catalog.pg_class c\n"
594 " JOIN pg_catalog.pg_namespace ns"
595 " ON c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
596 " CROSS JOIN LATERAL (SELECT c.relkind IN ("
597 CppAsString2(RELKIND_PARTITIONED_TABLE) ", "
598 CppAsString2(RELKIND_PARTITIONED_INDEX) ")) as p (inherited)\n"
599 " LEFT JOIN pg_catalog.pg_class t"
600 " ON c.reltoastrelid OPERATOR(pg_catalog.=) t.oid\n");
601
602 /*
603 * Used to match the tables or schemas listed by the user, completing the
604 * JOIN clause.
605 */
606 if (objects_listed)
607 {
608 appendPQExpBufferStr(&catalog_query, " LEFT JOIN listed_objects"
609 " ON listed_objects.object_oid"
610 " OPERATOR(pg_catalog.=) ");
611
612 if (vacopts->objfilter & OBJFILTER_TABLE)
613 appendPQExpBufferStr(&catalog_query, "c.oid\n");
614 else
615 appendPQExpBufferStr(&catalog_query, "ns.oid\n");
616 }
617
618 /*
619 * Exclude temporary tables, beginning the WHERE clause.
620 */
621 appendPQExpBufferStr(&catalog_query,
622 " WHERE c.relpersistence OPERATOR(pg_catalog.!=) "
623 CppAsString2(RELPERSISTENCE_TEMP) "\n");
624
625 /*
626 * Used to match the tables or schemas listed by the user, for the WHERE
627 * clause.
628 */
629 if (objects_listed)
630 {
631 if (vacopts->objfilter & OBJFILTER_SCHEMA_EXCLUDE)
632 appendPQExpBufferStr(&catalog_query,
633 " AND listed_objects.object_oid IS NULL\n");
634 else
635 appendPQExpBufferStr(&catalog_query,
636 " AND listed_objects.object_oid IS NOT NULL\n");
637 }
638
639 /*
640 * If no tables were listed, filter for the relevant relation types. If
641 * tables were given via --table, don't bother filtering by relation type.
642 * Instead, let the server decide whether a given relation can be
643 * processed in which case the user will know about it.
644 */
645 if ((vacopts->objfilter & OBJFILTER_TABLE) == 0)
646 {
647 /*
648 * vacuumdb should generally follow the behavior of the underlying
649 * VACUUM and ANALYZE commands. In MODE_ANALYZE mode, process regular
650 * tables, materialized views, and partitioned tables, just like
651 * ANALYZE (with no specific target tables) does. Otherwise, process
652 * only regular tables and materialized views, since VACUUM skips
653 * partitioned tables when no target tables are specified.
654 */
655 if (vacopts->mode == MODE_ANALYZE)
656 appendPQExpBufferStr(&catalog_query,
657 " AND c.relkind OPERATOR(pg_catalog.=) ANY (array["
658 CppAsString2(RELKIND_RELATION) ", "
659 CppAsString2(RELKIND_MATVIEW) ", "
660 CppAsString2(RELKIND_PARTITIONED_TABLE) "])\n");
661 else
662 appendPQExpBufferStr(&catalog_query,
663 " AND c.relkind OPERATOR(pg_catalog.=) ANY (array["
664 CppAsString2(RELKIND_RELATION) ", "
665 CppAsString2(RELKIND_MATVIEW) "])\n");
666 }
667
668 /*
669 * For --min-xid-age and --min-mxid-age, the age of the relation is the
670 * greatest of the ages of the main relation and its associated TOAST
671 * table. The commands generated by vacuumdb will also process the TOAST
672 * table for the relation if necessary, so it does not need to be
673 * considered separately.
674 */
675 if (vacopts->min_xid_age != 0)
676 {
677 appendPQExpBuffer(&catalog_query,
678 " AND GREATEST(pg_catalog.age(c.relfrozenxid),"
679 " pg_catalog.age(t.relfrozenxid)) "
680 " OPERATOR(pg_catalog.>=) '%d'::pg_catalog.int4\n"
681 " AND c.relfrozenxid OPERATOR(pg_catalog.!=)"
682 " '0'::pg_catalog.xid\n",
683 vacopts->min_xid_age);
684 }
685
686 if (vacopts->min_mxid_age != 0)
687 {
688 appendPQExpBuffer(&catalog_query,
689 " AND GREATEST(pg_catalog.mxid_age(c.relminmxid),"
690 " pg_catalog.mxid_age(t.relminmxid)) OPERATOR(pg_catalog.>=)"
691 " '%d'::pg_catalog.int4\n"
692 " AND c.relminmxid OPERATOR(pg_catalog.!=)"
693 " '0'::pg_catalog.xid\n",
694 vacopts->min_mxid_age);
695 }
696
697 if (vacopts->missing_stats_only)
698 {
699 appendPQExpBufferStr(&catalog_query, " AND (\n");
700
701 /* regular stats */
702 appendPQExpBufferStr(&catalog_query,
703 " EXISTS (SELECT NULL FROM pg_catalog.pg_attribute a\n"
704 " WHERE a.attrelid OPERATOR(pg_catalog.=) c.oid\n"
705 " AND a.attnum OPERATOR(pg_catalog.>) 0::pg_catalog.int2\n"
706 " AND NOT a.attisdropped\n"
707 " AND a.attstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
708 " AND a.attgenerated OPERATOR(pg_catalog.<>) "
709 CppAsString2(ATTRIBUTE_GENERATED_VIRTUAL) "\n"
710 " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
711 " WHERE s.starelid OPERATOR(pg_catalog.=) a.attrelid\n"
712 " AND s.staattnum OPERATOR(pg_catalog.=) a.attnum\n"
713 " AND s.stainherit OPERATOR(pg_catalog.=) p.inherited))\n");
714
715 /* extended stats */
716 appendPQExpBufferStr(&catalog_query,
717 " OR EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext e\n"
718 " WHERE e.stxrelid OPERATOR(pg_catalog.=) c.oid\n"
719 " AND e.stxstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
720 " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext_data d\n"
721 " WHERE d.stxoid OPERATOR(pg_catalog.=) e.oid\n"
722 " AND d.stxdinherit OPERATOR(pg_catalog.=) p.inherited))\n");
723
724 /* expression indexes */
725 appendPQExpBufferStr(&catalog_query,
726 " OR EXISTS (SELECT NULL FROM pg_catalog.pg_attribute a\n"
727 " JOIN pg_catalog.pg_index i"
728 " ON i.indexrelid OPERATOR(pg_catalog.=) a.attrelid\n"
729 " WHERE i.indrelid OPERATOR(pg_catalog.=) c.oid\n"
730 " AND i.indkey[a.attnum OPERATOR(pg_catalog.-) 1::pg_catalog.int2]"
731 " OPERATOR(pg_catalog.=) 0::pg_catalog.int2\n"
732 " AND a.attnum OPERATOR(pg_catalog.>) 0::pg_catalog.int2\n"
733 " AND NOT a.attisdropped\n"
734 " AND a.attstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
735 " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
736 " WHERE s.starelid OPERATOR(pg_catalog.=) a.attrelid\n"
737 " AND s.staattnum OPERATOR(pg_catalog.=) a.attnum\n"
738 " AND s.stainherit OPERATOR(pg_catalog.=) p.inherited))\n");
739
740 /* inheritance and regular stats */
741 appendPQExpBufferStr(&catalog_query,
742 " OR EXISTS (SELECT NULL FROM pg_catalog.pg_attribute a\n"
743 " WHERE a.attrelid OPERATOR(pg_catalog.=) c.oid\n"
744 " AND a.attnum OPERATOR(pg_catalog.>) 0::pg_catalog.int2\n"
745 " AND NOT a.attisdropped\n"
746 " AND a.attstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
747 " AND a.attgenerated OPERATOR(pg_catalog.<>) "
748 CppAsString2(ATTRIBUTE_GENERATED_VIRTUAL) "\n"
749 " AND c.relhassubclass\n"
750 " AND NOT p.inherited\n"
751 " AND EXISTS (SELECT NULL FROM pg_catalog.pg_inherits h\n"
752 " WHERE h.inhparent OPERATOR(pg_catalog.=) c.oid)\n"
753 " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic s\n"
754 " WHERE s.starelid OPERATOR(pg_catalog.=) a.attrelid\n"
755 " AND s.staattnum OPERATOR(pg_catalog.=) a.attnum\n"
756 " AND s.stainherit))\n");
757
758 /* inheritance and extended stats */
759 appendPQExpBufferStr(&catalog_query,
760 " OR EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext e\n"
761 " WHERE e.stxrelid OPERATOR(pg_catalog.=) c.oid\n"
762 " AND e.stxstattarget IS DISTINCT FROM 0::pg_catalog.int2\n"
763 " AND c.relhassubclass\n"
764 " AND NOT p.inherited\n"
765 " AND EXISTS (SELECT NULL FROM pg_catalog.pg_inherits h\n"
766 " WHERE h.inhparent OPERATOR(pg_catalog.=) c.oid)\n"
767 " AND NOT EXISTS (SELECT NULL FROM pg_catalog.pg_statistic_ext_data d\n"
768 " WHERE d.stxoid OPERATOR(pg_catalog.=) e.oid\n"
769 " AND d.stxdinherit))\n");
770
771 appendPQExpBufferStr(&catalog_query, " )\n");
772 }
773
774 /*
775 * Execute the catalog query. We use the default search_path for this
776 * query for consistency with table lookups done elsewhere by the user.
777 */
778 appendPQExpBufferStr(&catalog_query, " ORDER BY c.relpages DESC;");
779 executeCommand(conn, "RESET search_path;", echo);
780 res = executeQuery(conn, catalog_query.data, echo);
781 termPQExpBuffer(&catalog_query);
783
784 /*
785 * Build qualified identifiers for each table, including the column list
786 * if given.
787 */
789 for (int i = 0; i < PQntuples(res); i++)
790 {
793 PQgetvalue(res, i, 0),
795
796 if (objects_listed && !PQgetisnull(res, i, 2))
798
799 simple_string_list_append(found_objs, buf.data);
801 }
803 PQclear(res);
804
805 return found_objs;
806}
void splitTableColumnsSpec(const char *spec, int encoding, char **table, const char **columns)
Definition: common.c:33
#define CppAsString2(x)
Definition: c.h:423
#define ALWAYS_SECURE_SEARCH_PATH_SQL
Definition: connect.h:25
PGresult * executeQuery(PGconn *conn, const char *query)
Definition: connectdb.c:278
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7794
int i
Definition: isn.c:77
#define PQgetvalue
Definition: libpq-be-fe.h:253
#define PQclear
Definition: libpq-be-fe.h:245
#define PQgetisnull
Definition: libpq-be-fe.h:255
#define PQntuples
Definition: libpq-be-fe.h:251
void * palloc0(Size size)
Definition: mcxt.c:1395
static void executeCommand(PGconn *conn, const char *query)
Definition: pg_dumpall.c:1780
static char * buf
Definition: pg_test_fsync.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
void simple_string_list_append(SimpleStringList *list, const char *val)
Definition: simple_list.c:63
const char * fmtQualifiedIdEnc(const char *schema, const char *id, int encoding)
Definition: string_utils.c:263
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
Definition: string_utils.c:446
char val[FLEXIBLE_ARRAY_MEMBER]
Definition: simple_list.h:37
struct SimpleStringListCell * next
Definition: simple_list.h:34
SimpleStringListCell * head
Definition: simple_list.h:42
bits32 objfilter
Definition: vacuuming.h:35
bool missing_stats_only
Definition: vacuuming.h:53
#define OBJFILTER_SCHEMA_EXCLUDE
Definition: vacuuming.h:61
#define OBJFILTER_TABLE
Definition: vacuuming.h:59
#define OBJFILTER_SCHEMA
Definition: vacuuming.h:60

References ALWAYS_SECURE_SEARCH_PATH_SQL, appendPQExpBuffer(), appendPQExpBufferStr(), appendStringLiteralConn(), buf, conn, CppAsString2, PQExpBufferData::data, executeCommand(), executeQuery(), fmtQualifiedIdEnc(), SimpleStringList::head, i, initPQExpBuffer(), vacuumingOptions::min_mxid_age, vacuumingOptions::min_xid_age, vacuumingOptions::missing_stats_only, vacuumingOptions::mode, MODE_ANALYZE, SimpleStringListCell::next, vacuumingOptions::objfilter, OBJFILTER_SCHEMA, OBJFILTER_SCHEMA_EXCLUDE, OBJFILTER_TABLE, palloc0(), pg_free(), PQclear, PQclientEncoding(), PQgetisnull, PQgetvalue, PQntuples, resetPQExpBuffer(), simple_string_list_append(), splitTableColumnsSpec(), termPQExpBuffer(), and SimpleStringListCell::val.

Referenced by vacuum_one_database().

◆ run_vacuum_command()

static void run_vacuum_command ( PGconn conn,
const char *  sql,
bool  echo,
const char *  table 
)
static

Definition at line 1004 of file vacuuming.c.

1006{
1007 bool status;
1008
1009 if (echo)
1010 printf("%s\n", sql);
1011
1012 status = PQsendQuery(conn, sql) == 1;
1013
1014 if (!status)
1015 {
1016 if (table)
1017 {
1018 pg_log_error("vacuuming of table \"%s\" in database \"%s\" failed: %s",
1020 }
1021 else
1022 {
1023 pg_log_error("vacuuming of database \"%s\" failed: %s",
1025 }
1026 }
1027}
char * PQdb(const PGconn *conn)
Definition: fe-connect.c:7538
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7704
int PQsendQuery(PGconn *conn, const char *query)
Definition: fe-exec.c:1433
#define pg_log_error(...)
Definition: logging.h:106
#define printf(...)
Definition: port.h:266

References conn, pg_log_error, PQdb(), PQerrorMessage(), PQsendQuery(), printf, and table.

Referenced by vacuum_one_database().

◆ vacuum_all_databases()

static int vacuum_all_databases ( ConnParams cparams,
vacuumingOptions vacopts,
SimpleStringList objects,
int  concurrentCons,
const char *  progname,
bool  echo,
bool  quiet 
)
static

Definition at line 435 of file vacuuming.c.

440{
441 int ret = EXIT_SUCCESS;
442 PGconn *conn;
443 PGresult *result;
444 int numdbs;
445
446 conn = connectMaintenanceDatabase(cparams, progname, echo);
447 result = executeQuery(conn,
448 "SELECT datname FROM pg_database WHERE datallowconn AND datconnlimit <> -2 ORDER BY 1;",
449 echo);
450 numdbs = PQntuples(result);
451 PQfinish(conn);
452
453 if (vacopts->mode == MODE_ANALYZE_IN_STAGES)
454 {
455 SimpleStringList **found_objs = NULL;
456
457 if (vacopts->missing_stats_only)
458 found_objs = palloc0(numdbs * sizeof(SimpleStringList *));
459
460 /*
461 * When analyzing all databases in stages, we analyze them all in the
462 * fastest stage first, so that initial statistics become available
463 * for all of them as soon as possible.
464 *
465 * This means we establish several times as many connections, but
466 * that's a secondary consideration.
467 */
468 for (int stage = 0; stage < ANALYZE_NUM_STAGES; stage++)
469 {
470 for (int i = 0; i < numdbs; i++)
471 {
472 cparams->override_dbname = PQgetvalue(result, i, 0);
473 ret = vacuum_one_database(cparams, vacopts, stage,
474 objects,
475 vacopts->missing_stats_only ? &found_objs[i] : NULL,
476 concurrentCons,
477 progname, echo, quiet);
478 if (ret != EXIT_SUCCESS)
479 break;
480 }
481 if (ret != EXIT_SUCCESS)
482 break;
483 }
484
485 if (vacopts->missing_stats_only)
486 {
487 for (int i = 0; i < numdbs; i++)
488 free_retrieved_objects(found_objs[i]);
489 pg_free(found_objs);
490 }
491 }
492 else
493 {
494 for (int i = 0; i < numdbs; i++)
495 {
496 cparams->override_dbname = PQgetvalue(result, i, 0);
497 ret = vacuum_one_database(cparams, vacopts,
499 objects,
500 NULL,
501 concurrentCons,
502 progname, echo, quiet);
503 if (ret != EXIT_SUCCESS)
504 break;
505 }
506 }
507
508 PQclear(result);
509
510 return ret;
511}
PGconn * connectMaintenanceDatabase(ConnParams *cparams, const char *progname, bool echo)
void PQfinish(PGconn *conn)
Definition: fe-connect.c:5316
const char * progname
Definition: main.c:44
#define EXIT_SUCCESS
Definition: settings.h:193
char * override_dbname
Definition: pg_backup.h:93
static void free_retrieved_objects(SimpleStringList *list)
Definition: vacuuming.c:815
static int vacuum_one_database(ConnParams *cparams, vacuumingOptions *vacopts, int stage, SimpleStringList *objects, SimpleStringList **found_objs, int concurrentCons, const char *progname, bool echo, bool quiet)
Definition: vacuuming.c:164
#define ANALYZE_NUM_STAGES
Definition: vacuuming.h:29
#define ANALYZE_NO_STAGE
Definition: vacuuming.h:28

References ANALYZE_NO_STAGE, ANALYZE_NUM_STAGES, conn, connectMaintenanceDatabase(), executeQuery(), EXIT_SUCCESS, free_retrieved_objects(), i, vacuumingOptions::missing_stats_only, vacuumingOptions::mode, MODE_ANALYZE_IN_STAGES, _connParams::override_dbname, palloc0(), pg_free(), PQclear, PQfinish(), PQgetvalue, PQntuples, progname, and vacuum_one_database().

Referenced by vacuuming_main().

◆ vacuum_one_database()

static int vacuum_one_database ( ConnParams cparams,
vacuumingOptions vacopts,
int  stage,
SimpleStringList objects,
SimpleStringList **  found_objs,
int  concurrentCons,
const char *  progname,
bool  echo,
bool  quiet 
)
static

Definition at line 164 of file vacuuming.c.

171{
172 PQExpBufferData sql;
173 PGconn *conn;
176 int ntups = 0;
177 const char *initcmd;
178 SimpleStringList *retobjs = NULL;
179 bool free_retobjs = false;
180 int ret = EXIT_SUCCESS;
181 const char *stage_commands[] = {
182 "SET default_statistics_target=1; SET vacuum_cost_delay=0;",
183 "SET default_statistics_target=10; RESET vacuum_cost_delay;",
184 "RESET default_statistics_target;"
185 };
186 const char *stage_messages[] = {
187 gettext_noop("Generating minimal optimizer statistics (1 target)"),
188 gettext_noop("Generating medium optimizer statistics (10 targets)"),
189 gettext_noop("Generating default (full) optimizer statistics")
190 };
191
192 Assert(stage == ANALYZE_NO_STAGE ||
193 (stage >= 0 && stage < ANALYZE_NUM_STAGES));
194
195 conn = connectDatabase(cparams, progname, echo, false, true);
196
197 if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
198 {
199 PQfinish(conn);
200 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
201 "disable-page-skipping", "9.6");
202 }
203
204 if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
205 {
206 PQfinish(conn);
207 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
208 "no-index-cleanup", "12");
209 }
210
211 if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
212 {
213 PQfinish(conn);
214 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
215 "force-index-cleanup", "12");
216 }
217
218 if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
219 {
220 PQfinish(conn);
221 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
222 "no-truncate", "12");
223 }
224
225 if (!vacopts->process_main && PQserverVersion(conn) < 160000)
226 {
227 PQfinish(conn);
228 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
229 "no-process-main", "16");
230 }
231
232 if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
233 {
234 PQfinish(conn);
235 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
236 "no-process-toast", "14");
237 }
238
239 if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
240 {
241 PQfinish(conn);
242 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
243 "skip-locked", "12");
244 }
245
246 if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
247 {
248 PQfinish(conn);
249 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
250 "--min-xid-age", "9.6");
251 }
252
253 if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
254 {
255 PQfinish(conn);
256 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
257 "--min-mxid-age", "9.6");
258 }
259
260 if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
261 {
262 PQfinish(conn);
263 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
264 "--parallel", "13");
265 }
266
267 if (vacopts->buffer_usage_limit && PQserverVersion(conn) < 160000)
268 {
269 PQfinish(conn);
270 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
271 "--buffer-usage-limit", "16");
272 }
273
274 if (vacopts->missing_stats_only && PQserverVersion(conn) < 150000)
275 {
276 PQfinish(conn);
277 pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
278 "--missing-stats-only", "15");
279 }
280
281 /* skip_database_stats is used automatically if server supports it */
282 vacopts->skip_database_stats = (PQserverVersion(conn) >= 160000);
283
284 if (!quiet)
285 {
286 if (vacopts->mode == MODE_ANALYZE_IN_STAGES)
287 printf(_("%s: processing database \"%s\": %s\n"),
288 progname, PQdb(conn), _(stage_messages[stage]));
289 else
290 printf(_("%s: vacuuming database \"%s\"\n"),
291 progname, PQdb(conn));
292 fflush(stdout);
293 }
294
295 /*
296 * If the caller provided the results of a previous catalog query, just
297 * use that. Otherwise, run the catalog query ourselves and set the
298 * return variable if provided. (If it is, then freeing the string list
299 * becomes the caller's responsibility.)
300 */
301 if (found_objs && *found_objs)
302 retobjs = *found_objs;
303 else
304 {
305 retobjs = retrieve_objects(conn, vacopts, objects, echo);
306 if (found_objs)
307 *found_objs = retobjs;
308 else
309 free_retobjs = true;
310 }
311
312 /*
313 * Count the number of objects in the catalog query result. If there are
314 * none, we are done.
315 */
316 for (cell = retobjs->head; cell; cell = cell->next)
317 ntups++;
318
319 if (ntups == 0)
320 {
321 PQfinish(conn);
322 if (free_retobjs)
323 free_retrieved_objects(retobjs);
324 return EXIT_SUCCESS;
325 }
326
327 /*
328 * Ensure concurrentCons is sane. If there are more connections than
329 * vacuumable relations, we don't need to use them all.
330 */
331 if (concurrentCons > ntups)
332 concurrentCons = ntups;
333 if (concurrentCons <= 0)
334 concurrentCons = 1;
335
336 /*
337 * All slots need to be prepared to run the appropriate analyze stage, if
338 * caller requested that mode. We have to prepare the initial connection
339 * ourselves before setting up the slots.
340 */
341 if (vacopts->mode == MODE_ANALYZE_IN_STAGES)
342 {
343 initcmd = stage_commands[stage];
344 executeCommand(conn, initcmd, echo);
345 }
346 else
347 initcmd = NULL;
348
349 /*
350 * Setup the database connections. We reuse the connection we already have
351 * for the first slot. If not in parallel mode, the first slot in the
352 * array contains the connection.
353 */
354 sa = ParallelSlotsSetup(concurrentCons, cparams, progname, echo, initcmd);
356
357 initPQExpBuffer(&sql);
358
359 cell = retobjs->head;
360 do
361 {
362 const char *tabname = cell->val;
363 ParallelSlot *free_slot;
364
365 if (CancelRequested)
366 {
367 ret = EXIT_FAILURE;
368 goto finish;
369 }
370
371 free_slot = ParallelSlotsGetIdle(sa, NULL);
372 if (!free_slot)
373 {
374 ret = EXIT_FAILURE;
375 goto finish;
376 }
377
378 prepare_vacuum_command(free_slot->connection, &sql,
379 vacopts, tabname);
380
381 /*
382 * Execute the vacuum. All errors are handled in processQueryResult
383 * through ParallelSlotsGetIdle.
384 */
386 run_vacuum_command(free_slot->connection, sql.data,
387 echo, tabname);
388
389 cell = cell->next;
390 } while (cell != NULL);
391
393 {
394 ret = EXIT_FAILURE;
395 goto finish;
396 }
397
398 /* If we used SKIP_DATABASE_STATS, mop up with ONLY_DATABASE_STATS */
399 if (vacopts->mode == MODE_VACUUM && vacopts->skip_database_stats)
400 {
401 const char *cmd = "VACUUM (ONLY_DATABASE_STATS);";
402 ParallelSlot *free_slot = ParallelSlotsGetIdle(sa, NULL);
403
404 if (!free_slot)
405 {
406 ret = EXIT_FAILURE;
407 goto finish;
408 }
409
411 run_vacuum_command(free_slot->connection, cmd, echo, NULL);
412
414 ret = EXIT_FAILURE; /* error already reported by handler */
415 }
416
417finish:
419 pg_free(sa);
420 termPQExpBuffer(&sql);
421 if (free_retobjs)
422 free_retrieved_objects(retobjs);
423
424 return ret;
425}
#define gettext_noop(x)
Definition: c.h:1184
volatile sig_atomic_t CancelRequested
Definition: cancel.c:59
PGconn * connectDatabase(const ConnParams *cparams, const char *progname, bool echo, bool fail_ok, bool allow_password_reuse)
Definition: connect_utils.c:32
#define _(x)
Definition: elog.c:91
ParallelSlotArray * ParallelSlotsSetup(int numslots, ConnParams *cparams, const char *progname, bool echo, const char *initcmd)
bool ParallelSlotsWaitCompletion(ParallelSlotArray *sa)
bool TableCommandResultHandler(PGresult *res, PGconn *conn, void *context)
ParallelSlot * ParallelSlotsGetIdle(ParallelSlotArray *sa, const char *dbname)
void ParallelSlotsTerminate(ParallelSlotArray *sa)
void ParallelSlotsAdoptConn(ParallelSlotArray *sa, PGconn *conn)
static void ParallelSlotSetHandler(ParallelSlot *slot, ParallelSlotResultHandler handler, void *context)
Definition: parallel_slot.h:47
#define EXIT_FAILURE
Definition: settings.h:197
PGconn * connection
Definition: parallel_slot.h:23
static SimpleStringList * retrieve_objects(PGconn *conn, vacuumingOptions *vacopts, SimpleStringList *objects, bool echo)
Definition: vacuuming.c:526
static void prepare_vacuum_command(PGconn *conn, PQExpBuffer sql, vacuumingOptions *vacopts, const char *table)
Definition: vacuuming.c:832
static void run_vacuum_command(PGconn *conn, const char *sql, bool echo, const char *table)
Definition: vacuuming.c:1004
@ MODE_VACUUM
Definition: vacuuming.h:22

References _, ANALYZE_NO_STAGE, ANALYZE_NUM_STAGES, Assert(), vacuumingOptions::buffer_usage_limit, CancelRequested, conn, connectDatabase(), ParallelSlot::connection, PQExpBufferData::data, vacuumingOptions::disable_page_skipping, vacuumingOptions::do_truncate, executeCommand(), EXIT_FAILURE, EXIT_SUCCESS, vacuumingOptions::force_index_cleanup, free_retrieved_objects(), gettext_noop, SimpleStringList::head, initPQExpBuffer(), vacuumingOptions::min_mxid_age, vacuumingOptions::min_xid_age, vacuumingOptions::missing_stats_only, vacuumingOptions::mode, MODE_ANALYZE_IN_STAGES, MODE_VACUUM, SimpleStringListCell::next, vacuumingOptions::no_index_cleanup, vacuumingOptions::parallel_workers, ParallelSlotsAdoptConn(), ParallelSlotSetHandler(), ParallelSlotsGetIdle(), ParallelSlotsSetup(), ParallelSlotsTerminate(), ParallelSlotsWaitCompletion(), pg_fatal, pg_free(), PQdb(), PQfinish(), PQserverVersion(), prepare_vacuum_command(), printf, vacuumingOptions::process_main, vacuumingOptions::process_toast, progname, retrieve_objects(), run_vacuum_command(), vacuumingOptions::skip_database_stats, vacuumingOptions::skip_locked, generate_unaccent_rules::stdout, TableCommandResultHandler(), termPQExpBuffer(), and SimpleStringListCell::val.

Referenced by vacuum_all_databases(), and vacuuming_main().

◆ vacuuming_main()

int vacuuming_main ( ConnParams cparams,
const char *  dbname,
const char *  maintenance_db,
vacuumingOptions vacopts,
SimpleStringList objects,
unsigned int  tbl_count,
int  concurrentCons,
const char *  progname,
bool  echo,
bool  quiet 
)

Definition at line 55 of file vacuuming.c.

60{
62
63 /* Avoid opening extra connections. */
64 if (tbl_count > 0 && (concurrentCons > tbl_count))
65 concurrentCons = tbl_count;
66
67 if (vacopts->objfilter & OBJFILTER_ALL_DBS)
68 {
69 cparams->dbname = maintenance_db;
70
71 return vacuum_all_databases(cparams, vacopts,
72 objects,
73 concurrentCons,
74 progname, echo, quiet);
75 }
76 else
77 {
78 if (dbname == NULL)
79 {
80 if (getenv("PGDATABASE"))
81 dbname = getenv("PGDATABASE");
82 else if (getenv("PGUSER"))
83 dbname = getenv("PGUSER");
84 else
86 }
87
88 cparams->dbname = dbname;
89
90 if (vacopts->mode == MODE_ANALYZE_IN_STAGES)
91 {
92 SimpleStringList *found_objs = NULL;
93
94 for (int stage = 0; stage < ANALYZE_NUM_STAGES; stage++)
95 {
96 int ret;
97
98 ret = vacuum_one_database(cparams, vacopts,
99 stage,
100 objects,
101 vacopts->missing_stats_only ? &found_objs : NULL,
102 concurrentCons,
103 progname, echo, quiet);
104 if (ret != 0)
105 {
106 free_retrieved_objects(found_objs);
107 return ret;
108 }
109 }
110
111 free_retrieved_objects(found_objs);
112 return EXIT_SUCCESS;
113 }
114 else
115 return vacuum_one_database(cparams, vacopts,
117 objects, NULL,
118 concurrentCons,
119 progname, echo, quiet);
120 }
121}
void setup_cancel_handler(void(*query_cancel_callback)(void))
Definition: cancel.c:183
char * dbname
Definition: streamutil.c:49
char * dbname
Definition: pg_backup.h:86
const char * get_user_name_or_exit(const char *progname)
Definition: username.c:74
static int vacuum_all_databases(ConnParams *cparams, vacuumingOptions *vacopts, SimpleStringList *objects, int concurrentCons, const char *progname, bool echo, bool quiet)
Definition: vacuuming.c:435
#define OBJFILTER_ALL_DBS
Definition: vacuuming.h:57

References ANALYZE_NO_STAGE, ANALYZE_NUM_STAGES, dbname, _connParams::dbname, EXIT_SUCCESS, free_retrieved_objects(), get_user_name_or_exit(), vacuumingOptions::missing_stats_only, vacuumingOptions::mode, MODE_ANALYZE_IN_STAGES, vacuumingOptions::objfilter, OBJFILTER_ALL_DBS, progname, setup_cancel_handler(), vacuum_all_databases(), and vacuum_one_database().

Referenced by main().