diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index 10b3a9a..05882c3 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -27,6 +27,7 @@ ANALYZE [ VERBOSE ] [ table_and_columnswhere option can be one of: VERBOSE + NOWAIT and table_and_columns is: @@ -77,6 +78,17 @@ ANALYZE [ VERBOSE ] [ table_and_columns + NOWAIT + + + Specifies that ANALYZE should not wait for any + conflicting locks to be released: if a relation cannot be locked + immediately without waiting, the relation is skipped. + + + + + table_name diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index b760e8e..1d7ffef 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -31,6 +31,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_and_columns is: @@ -161,6 +162,17 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ table_name diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index f952b3c..e3e2444 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -159,13 +159,22 @@ analyze_rel(Oid relid, RangeVar *relation, int options, */ if (!onerel) { + char *relname = NULL; + /* - * If the RangeVar is not defined, we do not have enough information - * to provide a meaningful log statement. Chances are that - * analyze_rel's caller has intentionally not provided this - * information so that this logging is skipped, anyway. + * If relation is NULL, we do not have enough information to provide a + * meaningful log statement. Chances are that this information was + * intentionally not provided so that this logging is skipped, anyway. + * However, if VACOPT_NOWAIT is specified, we always try to emit a log + * statement if a relation is skipped, even if the provided RangeVar + * is NULL. */ - if (relation == NULL) + if (relation != NULL) + relname = relation->relname; + else if (!rel_lock) + relname = get_rel_name(relid); + + if (relname == NULL) return; /* @@ -185,12 +194,12 @@ analyze_rel(Oid relid, RangeVar *relation, int options, ereport(elevel, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("skipping analyze of \"%s\" --- lock not available", - relation->relname))); + relname))); else ereport(elevel, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("skipping analyze of \"%s\" --- relation no longer exists", - relation->relname))); + relname))); return; } diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 4abe6b1..60ac885 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -47,6 +47,7 @@ #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/guc.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/snapmgr.h" #include "utils/syscache.h" @@ -1411,21 +1412,29 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) if (!onerel) { int elevel = 0; + char *relname = NULL; + + /* + * If relation is NULL, we do not have enough information to provide a + * meaningful log statement. Chances are that this information was + * intentionally not provided so that this logging is skipped, anyway. + * However, if VACOPT_NOWAIT is specified, we always try to emit a log + * statement if a relation is skipped, even if the provided RangeVar + * is NULL. + */ + if (relation != NULL) + relname = relation->relname; + else if (!rel_lock) + relname = get_rel_name(relid); /* * Determine the log level. * - * If the RangeVar is not defined, we do not have enough information - * to provide a meaningful log statement. Chances are that - * vacuum_rel's caller has intentionally not provided this information - * so that this logging is skipped, anyway. - * - * Otherwise, for autovacuum logs, we emit a LOG if - * log_autovacuum_min_duration is not disabled. For manual VACUUM, we - * emit a WARNING to match the log statements in the permission - * checks. + * For autovacuum logs, we emit a LOG if log_autovacuum_min_duration + * is not disabled. For manual VACUUM, we emit a WARNING to match the + * log statements in the permission checks. */ - if (relation != NULL) + if (relname != NULL) { if (!IsAutoVacuumWorkerProcess()) elevel = WARNING; @@ -1439,12 +1448,12 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params) ereport(elevel, (errcode(ERRCODE_LOCK_NOT_AVAILABLE), errmsg("skipping vacuum of \"%s\" --- lock not available", - relation->relname))); + relname))); else ereport(elevel, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("skipping vacuum of \"%s\" --- relation no longer exists", - relation->relname))); + relname))); } PopActiveSnapshot(); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d0a5203..f7d5fb5 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10507,6 +10507,7 @@ vacuum_option_elem: errmsg("unrecognized VACUUM option \"%s\"", $1), parser_errposition(@1))); } + | NOWAIT { $$ = VACOPT_NOWAIT; } ; AnalyzeStmt: analyze_keyword opt_verbose opt_vacuum_relation_list @@ -10534,6 +10535,7 @@ analyze_option_list: analyze_option_elem: VERBOSE { $$ = VACOPT_VERBOSE; } + | NOWAIT { $$ = VACOPT_NOWAIT; } ; analyze_keyword: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 2eaa6b2..27e06fb 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -3116,7 +3116,7 @@ typedef enum VacuumOption VACOPT_VERBOSE = 1 << 2, /* print progress info */ VACOPT_FREEZE = 1 << 3, /* FREEZE option */ VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ - VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */ + VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock */ VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ } VacuumOption; diff --git a/src/test/isolation/expected/vacuum-nowait.out b/src/test/isolation/expected/vacuum-nowait.out new file mode 100644 index 0000000..a65f8be --- /dev/null +++ b/src/test/isolation/expected/vacuum-nowait.out @@ -0,0 +1,67 @@ +Parsed test spec with 2 sessions + +starting permutation: lock vac_specified commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping vacuum of "part1" --- lock not available +step vac_specified: VACUUM (NOWAIT) part1, part2; +step commit: + COMMIT; + + +starting permutation: lock vac_all_parts commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping vacuum of "part1" --- lock not available +step vac_all_parts: VACUUM (NOWAIT) parted; +step commit: + COMMIT; + + +starting permutation: lock analyze_specified commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping analyze of "part1" --- lock not available +step analyze_specified: ANALYZE (NOWAIT) part1, part2; +step commit: + COMMIT; + + +starting permutation: lock analyze_all_parts commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping analyze of "part1" --- lock not available +step analyze_all_parts: ANALYZE (NOWAIT) parted; +step commit: + COMMIT; + + +starting permutation: lock vac_analyze_specified commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping vacuum of "part1" --- lock not available +step vac_analyze_specified: VACUUM (ANALYZE, NOWAIT) part1, part2; +step commit: + COMMIT; + + +starting permutation: lock vac_analyze_all_parts commit +step lock: + BEGIN; + LOCK part1 IN SHARE MODE; + +WARNING: skipping vacuum of "part1" --- lock not available +step vac_analyze_all_parts: VACUUM (ANALYZE, NOWAIT) parted; +step commit: + COMMIT; + diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index eb566eb..c1c09ea 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -64,3 +64,4 @@ test: async-notify test: vacuum-reltuples test: timeouts test: vacuum-concurrent-drop +test: vacuum-nowait diff --git a/src/test/isolation/specs/vacuum-nowait.spec b/src/test/isolation/specs/vacuum-nowait.spec new file mode 100644 index 0000000..ed27d4f --- /dev/null +++ b/src/test/isolation/specs/vacuum-nowait.spec @@ -0,0 +1,42 @@ +# Test for the NOWAIT option for VACUUM and ANALYZE commands. +# +# Skipped relations should be logged regardless of whether it was explicitly +# specified in the command. + +setup +{ + CREATE TABLE parted (a INT) PARTITION BY LIST (a); + CREATE TABLE part1 PARTITION OF parted FOR VALUES IN (1); + CREATE TABLE part2 PARTITION OF parted FOR VALUES IN (2); +} + +teardown +{ + DROP TABLE IF EXISTS parted; +} + +session "s1" +step "lock" +{ + BEGIN; + LOCK part1 IN SHARE MODE; +} +step "commit" +{ + COMMIT; +} + +session "s2" +step "vac_specified" { VACUUM (NOWAIT) part1, part2; } +step "vac_all_parts" { VACUUM (NOWAIT) parted; } +step "analyze_specified" { ANALYZE (NOWAIT) part1, part2; } +step "analyze_all_parts" { ANALYZE (NOWAIT) parted; } +step "vac_analyze_specified" { VACUUM (ANALYZE, NOWAIT) part1, part2; } +step "vac_analyze_all_parts" { VACUUM (ANALYZE, NOWAIT) parted; } + +permutation "lock" "vac_specified" "commit" +permutation "lock" "vac_all_parts" "commit" +permutation "lock" "analyze_specified" "commit" +permutation "lock" "analyze_all_parts" "commit" +permutation "lock" "vac_analyze_specified" "commit" +permutation "lock" "vac_analyze_all_parts" "commit" diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index 199dda6..99aad21 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -113,8 +113,11 @@ ERROR: relation "does_not_exist" does not exist ANALYZE vactst (i), vacparted (does_not_exist); ERROR: column "does_not_exist" of relation "vacparted" does not exist -- parenthesized syntax for ANALYZE -ANALYZE (VERBOSE) does_not_exist; +ANALYZE (NOWAIT, VERBOSE) does_not_exist; ERROR: relation "does_not_exist" does not exist +-- nowait option +VACUUM (NOWAIT) vactst; +ANALYZE (NOWAIT) vactst; DROP TABLE vaccluster; DROP TABLE vactst; DROP TABLE vacparted; diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index e7b7a7f..5e88c68 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -90,7 +90,11 @@ ANALYZE vactst, does_not_exist, vacparted; ANALYZE vactst (i), vacparted (does_not_exist); -- parenthesized syntax for ANALYZE -ANALYZE (VERBOSE) does_not_exist; +ANALYZE (NOWAIT, VERBOSE) does_not_exist; + +-- nowait option +VACUUM (NOWAIT) vactst; +ANALYZE (NOWAIT) vactst; DROP TABLE vaccluster; DROP TABLE vactst;