Skip to content

Commit 1d7a459

Browse files
committedFeb 16, 2022
Merge branch 'release_2_5_5'
2 parents 98f77d2 + d222659 commit 1d7a459

File tree

8 files changed

+294
-47
lines changed

8 files changed

+294
-47
lines changed
 

‎doc/pgprobackup.xml

+27-8
Original file line numberDiff line numberDiff line change
@@ -4176,7 +4176,7 @@ pg_probackup restore -B <replaceable>backup_dir</replaceable> --instance <replac
41764176
pg_probackup checkdb
41774177
[-B <replaceable>backup_dir</replaceable>] [--instance <replaceable>instance_name</replaceable>] [-D <replaceable>data_dir</replaceable>]
41784178
[--help] [-j <replaceable>num_threads</replaceable>] [--progress]
4179-
[--skip-block-validation] [--amcheck] [--heapallindexed]
4179+
[--skip-block-validation] [--amcheck [--checkunique] [--heapallindexed]]
41804180
[<replaceable>connection_options</replaceable>] [<replaceable>logging_options</replaceable>]
41814181
</programlisting>
41824182
<para>
@@ -4195,17 +4195,24 @@ pg_probackup checkdb
41954195
extension or the <application>amcheck_next</application> extension
41964196
installed in the database to check its indexes. For databases
41974197
without <application>amcheck</application>, index verification will be skipped.
4198+
Additional options <option>--checkunique</option> and <option>--heapallindexed</option>
4199+
are effective depending on the version of <application>amcheck</application> installed.
41984200
</para>
41994201
</listitem>
42004202
</varlistentry>
42014203

42024204
<varlistentry>
4203-
<term><option>--skip-block-validation</option></term>
4205+
<term><option>--checkunique</option></term>
42044206
<listitem>
42054207
<para>
4206-
Skip validation of data files. You can use this flag only
4207-
together with the <option>--amcheck</option> flag, so that only logical
4208-
verification of indexes is performed.
4208+
Verifies unique constraints during logical verification of indexes.
4209+
You can use this flag only together with the <option>--amcheck</option> flag when
4210+
the <application>amcheck</application> extension is
4211+
installed in the database.
4212+
</para>
4213+
<para>
4214+
This verification is only possible if it is supported by the version of the
4215+
<application>amcheck</application> extension you are using.
42094216
</para>
42104217
</listitem>
42114218
</varlistentry>
@@ -4219,12 +4226,24 @@ pg_probackup checkdb
42194226
<option>--amcheck</option> flag.
42204227
</para>
42214228
<para>
4222-
This check is only possible if you are using the
4223-
<application>amcheck</application> extension of version 2.0 or higher, or
4224-
the <application>amcheck_next</application> extension of any version.
4229+
This check is only possible if it is supported by the version of the
4230+
<application>amcheck</application> extension you are using or
4231+
if the <application>amcheck_next</application> extension is used instead.
4232+
</para>
4233+
</listitem>
4234+
</varlistentry>
4235+
<varlistentry>
4236+
4237+
<term><option>--skip-block-validation</option></term>
4238+
<listitem>
4239+
<para>
4240+
Skip validation of data files. You can use this flag only
4241+
together with the <option>--amcheck</option> flag, so that only logical
4242+
verification of indexes is performed.
42254243
</para>
42264244
</listitem>
42274245
</varlistentry>
4246+
42284247
</variablelist>
42294248
</para>
42304249
<para>

‎src/checkdb.c

+93-31
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ typedef struct pg_indexEntry
8383
char *name;
8484
char *namespace;
8585
bool heapallindexed_is_supported;
86+
bool checkunique_is_supported;
8687
/* schema where amcheck extension is located */
8788
char *amcheck_nspname;
8889
/* lock for synchronization of parallel threads */
@@ -351,10 +352,14 @@ get_index_list(const char *dbname, bool first_db_with_amcheck,
351352
{
352353
PGresult *res;
353354
char *amcheck_nspname = NULL;
355+
char *amcheck_extname = NULL;
356+
char *amcheck_extversion = NULL;
354357
int i;
355358
bool heapallindexed_is_supported = false;
359+
bool checkunique_is_supported = false;
356360
parray *index_list = NULL;
357361

362+
/* Check amcheck extension version */
358363
res = pgut_execute(db_conn, "SELECT "
359364
"extname, nspname, extversion "
360365
"FROM pg_catalog.pg_namespace n "
@@ -379,24 +384,68 @@ get_index_list(const char *dbname, bool first_db_with_amcheck,
379384
return NULL;
380385
}
381386

387+
amcheck_extname = pgut_malloc(strlen(PQgetvalue(res, 0, 0)) + 1);
388+
strcpy(amcheck_extname, PQgetvalue(res, 0, 0));
382389
amcheck_nspname = pgut_malloc(strlen(PQgetvalue(res, 0, 1)) + 1);
383390
strcpy(amcheck_nspname, PQgetvalue(res, 0, 1));
391+
amcheck_extversion = pgut_malloc(strlen(PQgetvalue(res, 0, 2)) + 1);
392+
strcpy(amcheck_extversion, PQgetvalue(res, 0, 2));
393+
PQclear(res);
384394

385395
/* heapallindexed_is_supported is database specific */
386-
if (strcmp(PQgetvalue(res, 0, 2), "1.0") != 0 &&
387-
strcmp(PQgetvalue(res, 0, 2), "1") != 0)
396+
/* TODO this is wrong check, heapallindexed supported also in 1.1.1, 1.2 and 1.2.1... */
397+
if (strcmp(amcheck_extversion, "1.0") != 0 &&
398+
strcmp(amcheck_extversion, "1") != 0)
388399
heapallindexed_is_supported = true;
389400

390401
elog(INFO, "Amchecking database '%s' using extension '%s' "
391402
"version %s from schema '%s'",
392-
dbname, PQgetvalue(res, 0, 0),
393-
PQgetvalue(res, 0, 2), PQgetvalue(res, 0, 1));
403+
dbname, amcheck_extname,
404+
amcheck_extversion, amcheck_nspname);
394405

395406
if (!heapallindexed_is_supported && heapallindexed)
396407
elog(WARNING, "Extension '%s' version %s in schema '%s'"
397408
"do not support 'heapallindexed' option",
398-
PQgetvalue(res, 0, 0), PQgetvalue(res, 0, 2),
399-
PQgetvalue(res, 0, 1));
409+
amcheck_extname, amcheck_extversion,
410+
amcheck_nspname);
411+
412+
#ifndef PGPRO_EE
413+
/*
414+
* Will support when the vanilla patch will commited https://commitfest.postgresql.org/32/2976/
415+
*/
416+
checkunique_is_supported = false;
417+
#else
418+
/*
419+
* Check bt_index_check function signature to determine support of checkunique parameter
420+
* This can't be exactly checked by checking extension version,
421+
* For example, 1.1.1 and 1.2.1 supports this parameter, but 1.2 doesn't (PGPROEE-12.4.1)
422+
*/
423+
res = pgut_execute(db_conn, "SELECT "
424+
" oid "
425+
"FROM pg_catalog.pg_proc "
426+
"WHERE "
427+
" pronamespace = $1::regnamespace "
428+
"AND proname = 'bt_index_check' "
429+
"AND 'checkunique' = ANY(proargnames) "
430+
"AND (pg_catalog.string_to_array(proargtypes::text, ' ')::regtype[])[pg_catalog.array_position(proargnames, 'checkunique')] = 'bool'::regtype",
431+
1, (const char **) &amcheck_nspname);
432+
433+
if (PQresultStatus(res) != PGRES_TUPLES_OK)
434+
{
435+
PQclear(res);
436+
elog(ERROR, "Cannot check 'checkunique' option is supported in bt_index_check function %s: %s",
437+
dbname, PQerrorMessage(db_conn));
438+
}
439+
440+
checkunique_is_supported = PQntuples(res) >= 1;
441+
PQclear(res);
442+
#endif
443+
444+
if (!checkunique_is_supported && checkunique)
445+
elog(WARNING, "Extension '%s' version %s in schema '%s' "
446+
"do not support 'checkunique' parameter",
447+
amcheck_extname, amcheck_extversion,
448+
amcheck_nspname);
400449

401450
/*
402451
* In order to avoid duplicates, select global indexes
@@ -453,6 +502,7 @@ get_index_list(const char *dbname, bool first_db_with_amcheck,
453502
strcpy(ind->namespace, namespace); /* enough buffer size guaranteed */
454503

455504
ind->heapallindexed_is_supported = heapallindexed_is_supported;
505+
ind->checkunique_is_supported = checkunique_is_supported;
456506
ind->amcheck_nspname = pgut_malloc(strlen(amcheck_nspname) + 1);
457507
strcpy(ind->amcheck_nspname, amcheck_nspname);
458508
pg_atomic_clear_flag(&ind->lock);
@@ -464,6 +514,9 @@ get_index_list(const char *dbname, bool first_db_with_amcheck,
464514
}
465515

466516
PQclear(res);
517+
free(amcheck_extversion);
518+
free(amcheck_nspname);
519+
free(amcheck_extname);
467520

468521
return index_list;
469522
}
@@ -473,46 +526,54 @@ static bool
473526
amcheck_one_index(check_indexes_arg *arguments,
474527
pg_indexEntry *ind)
475528
{
476-
PGresult *res;
477-
char *params[2];
529+
PGresult *res;
530+
char *params[3];
531+
static const char *queries[] = {
532+
"SELECT %s.bt_index_check(index => $1)",
533+
"SELECT %s.bt_index_check(index => $1, heapallindexed => $2)",
534+
"SELECT %s.bt_index_check(index => $1, heapallindexed => $2, checkunique => $3)",
535+
};
536+
int params_count;
478537
char *query = NULL;
479538

480-
params[0] = palloc(64);
539+
if (interrupted)
540+
elog(ERROR, "Interrupted");
481541

542+
#define INDEXRELID 0
543+
#define HEAPALLINDEXED 1
544+
#define CHECKUNIQUE 2
482545
/* first argument is index oid */
483-
sprintf(params[0], "%u", ind->indexrelid);
546+
params[INDEXRELID] = palloc(64);
547+
sprintf(params[INDEXRELID], "%u", ind->indexrelid);
484548
/* second argument is heapallindexed */
485-
params[1] = heapallindexed ? "true" : "false";
549+
params[HEAPALLINDEXED] = heapallindexed ? "true" : "false";
550+
/* third optional argument is checkunique */
551+
params[CHECKUNIQUE] = checkunique ? "true" : "false";
552+
#undef CHECKUNIQUE
553+
#undef HEAPALLINDEXED
486554

487-
if (interrupted)
488-
elog(ERROR, "Interrupted");
489-
490-
if (ind->heapallindexed_is_supported)
491-
{
492-
query = palloc(strlen(ind->amcheck_nspname)+strlen("SELECT .bt_index_check($1, $2)")+1);
493-
sprintf(query, "SELECT %s.bt_index_check($1, $2)", ind->amcheck_nspname);
555+
params_count = ind->checkunique_is_supported ?
556+
3 :
557+
( ind->heapallindexed_is_supported ? 2 : 1 );
494558

495-
res = pgut_execute_parallel(arguments->conn_arg.conn,
496-
arguments->conn_arg.cancel_conn,
497-
query, 2, (const char **)params, true, true, true);
498-
}
499-
else
500-
{
501-
query = palloc(strlen(ind->amcheck_nspname)+strlen("SELECT .bt_index_check($1)")+1);
502-
sprintf(query, "SELECT %s.bt_index_check($1)", ind->amcheck_nspname);
559+
/*
560+
* Prepare query text with schema name
561+
* +1 for \0 and -2 for %s
562+
*/
563+
query = palloc(strlen(ind->amcheck_nspname) + strlen(queries[params_count - 1]) + 1 - 2);
564+
sprintf(query, queries[params_count - 1], ind->amcheck_nspname);
503565

504-
res = pgut_execute_parallel(arguments->conn_arg.conn,
566+
res = pgut_execute_parallel(arguments->conn_arg.conn,
505567
arguments->conn_arg.cancel_conn,
506-
query, 1, (const char **)params, true, true, true);
507-
}
568+
query, params_count, (const char **)params, true, true, true);
508569

509570
if (PQresultStatus(res) != PGRES_TUPLES_OK)
510571
{
511572
elog(WARNING, "Thread [%d]. Amcheck failed in database '%s' for index: '%s.%s': %s",
512573
arguments->thread_num, arguments->conn_opt.pgdatabase,
513574
ind->namespace, ind->name, PQresultErrorMessage(res));
514575

515-
pfree(params[0]);
576+
pfree(params[INDEXRELID]);
516577
pfree(query);
517578
PQclear(res);
518579
return false;
@@ -522,7 +583,8 @@ amcheck_one_index(check_indexes_arg *arguments,
522583
arguments->thread_num,
523584
arguments->conn_opt.pgdatabase, ind->namespace, ind->name);
524585

525-
pfree(params[0]);
586+
pfree(params[INDEXRELID]);
587+
#undef INDEXRELID
526588
pfree(query);
527589
PQclear(res);
528590
return true;

‎src/help.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ help_pg_probackup(void)
190190
printf(_("\n %s checkdb [-B backup-path] [--instance=instance_name]\n"), PROGRAM_NAME);
191191
printf(_(" [-D pgdata-path] [--progress] [-j num-threads]\n"));
192192
printf(_(" [--amcheck] [--skip-block-validation]\n"));
193-
printf(_(" [--heapallindexed]\n"));
193+
printf(_(" [--heapallindexed] [--checkunique]\n"));
194194
printf(_(" [--help]\n"));
195195

196196
printf(_("\n %s show -B backup-path\n"), PROGRAM_NAME);
@@ -601,7 +601,7 @@ help_checkdb(void)
601601
printf(_("\n%s checkdb [-B backup-path] [--instance=instance_name]\n"), PROGRAM_NAME);
602602
printf(_(" [-D pgdata-path] [-j num-threads] [--progress]\n"));
603603
printf(_(" [--amcheck] [--skip-block-validation]\n"));
604-
printf(_(" [--heapallindexed]\n\n"));
604+
printf(_(" [--heapallindexed] [--checkunique]\n\n"));
605605

606606
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
607607
printf(_(" --instance=instance_name name of the instance\n"));
@@ -616,6 +616,8 @@ help_checkdb(void)
616616
printf(_(" using 'amcheck' or 'amcheck_next' extensions\n"));
617617
printf(_(" --heapallindexed also check that heap is indexed\n"));
618618
printf(_(" can be used only with '--amcheck' option\n"));
619+
printf(_(" --checkunique also check unique constraints\n"));
620+
printf(_(" can be used only with '--amcheck' option\n"));
619621

620622
printf(_("\n Logging options:\n"));
621623
printf(_(" --log-level-console=log-level-console\n"));

‎src/pg_probackup.c

+12
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ static parray *exclude_relative_paths_list = NULL;
126126
/* checkdb options */
127127
bool need_amcheck = false;
128128
bool heapallindexed = false;
129+
bool checkunique = false;
129130
bool amcheck_parent = false;
130131

131132
/* delete options */
@@ -240,6 +241,7 @@ static ConfigOption cmd_options[] =
240241
/* checkdb options */
241242
{ 'b', 195, "amcheck", &need_amcheck, SOURCE_CMD_STRICT },
242243
{ 'b', 196, "heapallindexed", &heapallindexed, SOURCE_CMD_STRICT },
244+
{ 'b', 198, "checkunique", &checkunique, SOURCE_CMD_STRICT },
243245
{ 'b', 197, "parent", &amcheck_parent, SOURCE_CMD_STRICT },
244246
/* delete options */
245247
{ 'b', 145, "wal", &delete_wal, SOURCE_CMD_STRICT },
@@ -596,6 +598,16 @@ main(int argc, char *argv[])
596598
instance_config.pgdata == NULL)
597599
elog(ERROR, "required parameter not specified: --instance");
598600

601+
/* Check checkdb command options consistency */
602+
if (backup_subcmd == CHECKDB_CMD &&
603+
!need_amcheck)
604+
{
605+
if (heapallindexed)
606+
elog(ERROR, "--heapallindexed can only be used with --amcheck option");
607+
if (checkunique)
608+
elog(ERROR, "--checkunique can only be used with --amcheck option");
609+
}
610+
599611
/* Usually checkdb for file logging requires log_directory
600612
* to be specified explicitly, but if backup_dir and instance name are provided,
601613
* checkdb can use the usual default values or values from config

‎src/pg_probackup.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* pg_probackup.h: Backup/Recovery manager for PostgreSQL.
44
*
55
* Portions Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
6-
* Portions Copyright (c) 2015-2021, Postgres Professional
6+
* Portions Copyright (c) 2015-2022, Postgres Professional
77
*
88
*-------------------------------------------------------------------------
99
*/
@@ -338,7 +338,7 @@ typedef enum ShowFormat
338338
#define BYTES_INVALID (-1) /* file didn`t changed since previous backup, DELTA backup do not rely on it */
339339
#define FILE_NOT_FOUND (-2) /* file disappeared during backup */
340340
#define BLOCKNUM_INVALID (-1)
341-
#define PROGRAM_VERSION "2.5.4"
341+
#define PROGRAM_VERSION "2.5.5"
342342

343343
/* update when remote agent API or behaviour changes */
344344
#define AGENT_PROTOCOL_VERSION 20501
@@ -829,6 +829,7 @@ extern ShowFormat show_format;
829829

830830
/* checkdb options */
831831
extern bool heapallindexed;
832+
extern bool checkunique;
832833
extern bool skip_block_validation;
833834

834835
/* current settings */

‎tests/checkdb.py

+153-2
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def test_checkdb_amcheck_only_sanity(self):
211211

212212
# Clean after yourself
213213
gdb.kill()
214+
node.stop()
214215
self.del_test_dir(module_name, fname)
215216

216217
# @unittest.skip("skip")
@@ -349,6 +350,7 @@ def test_basic_checkdb_amcheck_only_sanity(self):
349350
log_file_content)
350351

351352
# Clean after yourself
353+
node.stop()
352354
self.del_test_dir(module_name, fname)
353355

354356
# @unittest.skip("skip")
@@ -445,6 +447,98 @@ def test_checkdb_block_validation_sanity(self):
445447
e.message)
446448

447449
# Clean after yourself
450+
node.stop()
451+
self.del_test_dir(module_name, fname)
452+
453+
def test_checkdb_checkunique(self):
454+
"""Test checkunique parameter of amcheck.bt_index_check function"""
455+
fname = self.id().split('.')[3]
456+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
457+
node = self.make_simple_node(
458+
base_dir=os.path.join(module_name, fname, 'node'),
459+
initdb_params=['--data-checksums'])
460+
node.slow_start()
461+
462+
try:
463+
node.safe_psql(
464+
"postgres",
465+
"create extension amcheck")
466+
except QueryException as e:
467+
node.safe_psql(
468+
"postgres",
469+
"create extension amcheck_next")
470+
471+
# Part of https://commitfest.postgresql.org/32/2976/ patch test
472+
node.safe_psql(
473+
"postgres",
474+
"CREATE TABLE bttest_unique(a varchar(50), b varchar(1500), c bytea, d varchar(50)); "
475+
"ALTER TABLE bttest_unique SET (autovacuum_enabled = false); "
476+
"CREATE UNIQUE INDEX bttest_unique_idx ON bttest_unique(a,b); "
477+
"UPDATE pg_catalog.pg_index SET indisunique = false "
478+
"WHERE indrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname = 'bttest_unique'); "
479+
"INSERT INTO bttest_unique "
480+
" SELECT i::text::varchar, "
481+
" array_to_string(array( "
482+
" SELECT substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ((random()*(36-1)+1)::integer), 1) "
483+
" FROM generate_series(1,1300)),'')::varchar, "
484+
" i::text::bytea, i::text::varchar "
485+
" FROM generate_series(0,1) AS i, generate_series(0,30) AS x; "
486+
"UPDATE pg_catalog.pg_index SET indisunique = true "
487+
"WHERE indrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname = 'bttest_unique'); "
488+
"DELETE FROM bttest_unique WHERE ctid::text='(0,2)'; "
489+
"DELETE FROM bttest_unique WHERE ctid::text='(4,2)'; "
490+
"DELETE FROM bttest_unique WHERE ctid::text='(4,3)'; "
491+
"DELETE FROM bttest_unique WHERE ctid::text='(9,3)';")
492+
493+
# run without checkunique option (error will not detected)
494+
output = self.checkdb_node(
495+
options=[
496+
'--amcheck',
497+
'--skip-block-validation',
498+
'-d', 'postgres', '-p', str(node.port)])
499+
500+
self.assertIn(
501+
'INFO: checkdb --amcheck finished successfully',
502+
output)
503+
self.assertIn(
504+
'All checked indexes are valid',
505+
output)
506+
507+
# run with checkunique option
508+
try:
509+
self.checkdb_node(
510+
options=[
511+
'--amcheck',
512+
'--skip-block-validation',
513+
'--checkunique',
514+
'-d', 'postgres', '-p', str(node.port)])
515+
if (ProbackupTest.enterprise and
516+
(self.get_version(node) >= 111300 and self.get_version(node) < 120000
517+
or self.get_version(node) >= 120800 and self.get_version(node) < 130000
518+
or self.get_version(node) >= 130400)):
519+
# we should die here because exception is what we expect to happen
520+
self.assertEqual(
521+
1, 0,
522+
"Expecting Error because of index corruption\n"
523+
" Output: {0} \n CMD: {1}".format(
524+
repr(self.output), self.cmd))
525+
else:
526+
self.assertRegex(
527+
self.output,
528+
r"WARNING: Extension 'amcheck(|_next)' version [\d.]* in schema 'public' do not support 'checkunique' parameter")
529+
except ProbackupException as e:
530+
self.assertIn(
531+
"ERROR: checkdb --amcheck finished with failure. Not all checked indexes are valid. All databases were amchecked.",
532+
e.message,
533+
"\n Unexpected Error Message: {0}\n CMD: {1}".format(
534+
repr(e.message), self.cmd))
535+
536+
self.assertIn(
537+
"Amcheck failed in database 'postgres' for index: 'public.bttest_unique_idx': ERROR: index \"bttest_unique_idx\" is corrupted. There are tuples violating UNIQUE constraint",
538+
e.message)
539+
540+
# Clean after yourself
541+
node.stop()
448542
self.del_test_dir(module_name, fname)
449543

450544
# @unittest.skip("skip")
@@ -502,6 +596,7 @@ def test_checkdb_sigint_handling(self):
502596

503597
# Clean after yourself
504598
gdb.kill()
599+
node.stop()
505600
self.del_test_dir(module_name, fname)
506601

507602
# @unittest.skip("skip")
@@ -563,12 +658,15 @@ def test_checkdb_with_least_privileges(self):
563658
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
564659
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
565660
'GRANT EXECUTE ON FUNCTION pg_catalog.set_config(text, text, boolean) TO backup; '
661+
'GRANT EXECUTE ON FUNCTION pg_catalog.texteq(text, text) TO backup; '
566662
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
567663
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
568664
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
569665
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
570666
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
571667
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
668+
'GRANT EXECUTE ON FUNCTION pg_catalog.string_to_array(text, text) TO backup; '
669+
'GRANT EXECUTE ON FUNCTION pg_catalog.array_position(anyarray, anyelement) TO backup; '
572670
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;' # amcheck-next function
573671
)
574672
# PG 9.6
@@ -588,13 +686,16 @@ def test_checkdb_with_least_privileges(self):
588686
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
589687
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
590688
'GRANT EXECUTE ON FUNCTION pg_catalog.set_config(text, text, boolean) TO backup; '
689+
'GRANT EXECUTE ON FUNCTION pg_catalog.texteq(text, text) TO backup; '
591690
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
592691
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
593692
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
594693
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
595694
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
596695
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
597696
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_system() TO backup; '
697+
'GRANT EXECUTE ON FUNCTION pg_catalog.string_to_array(text, text) TO backup; '
698+
'GRANT EXECUTE ON FUNCTION pg_catalog.array_position(anyarray, anyelement) TO backup; '
598699
# 'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup; '
599700
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;'
600701
)
@@ -615,13 +716,16 @@ def test_checkdb_with_least_privileges(self):
615716
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
616717
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
617718
'GRANT EXECUTE ON FUNCTION pg_catalog.set_config(text, text, boolean) TO backup; '
719+
'GRANT EXECUTE ON FUNCTION pg_catalog.texteq(text, text) TO backup; '
618720
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
619721
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
620722
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
621723
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
622724
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
623725
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
624-
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_system() TO backup;'
726+
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_system() TO backup; '
727+
'GRANT EXECUTE ON FUNCTION pg_catalog.string_to_array(text, text) TO backup; '
728+
'GRANT EXECUTE ON FUNCTION pg_catalog.array_position(anyarray, anyelement) TO backup;'
625729
)
626730
if ProbackupTest.enterprise:
627731
# amcheck-1.1
@@ -633,7 +737,45 @@ def test_checkdb_with_least_privileges(self):
633737
node.safe_psql(
634738
'backupdb',
635739
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup')
636-
# >= 11
740+
# >= 11 < 14
741+
elif self.get_version(node) > 110000 and self.get_version(node) < 140000:
742+
node.safe_psql(
743+
'backupdb',
744+
'CREATE ROLE backup WITH LOGIN; '
745+
'GRANT CONNECT ON DATABASE backupdb to backup; '
746+
'GRANT USAGE ON SCHEMA pg_catalog TO backup; '
747+
'GRANT USAGE ON SCHEMA public TO backup; '
748+
'GRANT SELECT ON TABLE pg_catalog.pg_proc TO backup; '
749+
'GRANT SELECT ON TABLE pg_catalog.pg_extension TO backup; '
750+
'GRANT SELECT ON TABLE pg_catalog.pg_database TO backup; '
751+
'GRANT SELECT ON TABLE pg_catalog.pg_am TO backup; '
752+
'GRANT SELECT ON TABLE pg_catalog.pg_class TO backup; '
753+
'GRANT SELECT ON TABLE pg_catalog.pg_index TO backup; '
754+
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
755+
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
756+
'GRANT EXECUTE ON FUNCTION pg_catalog.set_config(text, text, boolean) TO backup; '
757+
'GRANT EXECUTE ON FUNCTION pg_catalog.texteq(text, text) TO backup; '
758+
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
759+
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
760+
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
761+
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
762+
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
763+
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
764+
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_system() TO backup; '
765+
'GRANT EXECUTE ON FUNCTION pg_catalog.string_to_array(text, text) TO backup; '
766+
'GRANT EXECUTE ON FUNCTION pg_catalog.array_position(anyarray, anyelement) TO backup; '
767+
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup; '
768+
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;'
769+
)
770+
# checkunique parameter
771+
if ProbackupTest.enterprise:
772+
if (self.get_version(node) >= 111300 and self.get_version(node) < 120000
773+
or self.get_version(node) >= 120800 and self.get_version(node) < 130000
774+
or self.get_version(node) >= 130400):
775+
node.safe_psql(
776+
"backupdb",
777+
"GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool, bool) TO backup")
778+
# >= 14
637779
else:
638780
node.safe_psql(
639781
'backupdb',
@@ -650,16 +792,24 @@ def test_checkdb_with_least_privileges(self):
650792
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
651793
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
652794
'GRANT EXECUTE ON FUNCTION pg_catalog.set_config(text, text, boolean) TO backup; '
795+
'GRANT EXECUTE ON FUNCTION pg_catalog.texteq(text, text) TO backup; '
653796
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
654797
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
655798
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
656799
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
657800
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
658801
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
659802
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_control_system() TO backup; '
803+
'GRANT EXECUTE ON FUNCTION pg_catalog.string_to_array(text, text) TO backup; '
804+
'GRANT EXECUTE ON FUNCTION pg_catalog.array_position(anycompatiblearray, anycompatible) TO backup; '
660805
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup; '
661806
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;'
662807
)
808+
# checkunique parameter
809+
if ProbackupTest.enterprise:
810+
node.safe_psql(
811+
"backupdb",
812+
"GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool, bool) TO backup")
663813

664814
if ProbackupTest.enterprise:
665815
node.safe_psql(
@@ -700,4 +850,5 @@ def test_checkdb_with_least_privileges(self):
700850
repr(e.message), self.cmd))
701851

702852
# Clean after yourself
853+
node.stop()
703854
self.del_test_dir(module_name, fname)

‎tests/expected/option_help.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pg_probackup - utility to manage backup/recovery of PostgreSQL database.
107107
pg_probackup checkdb [-B backup-path] [--instance=instance_name]
108108
[-D pgdata-path] [--progress] [-j num-threads]
109109
[--amcheck] [--skip-block-validation]
110-
[--heapallindexed]
110+
[--heapallindexed] [--checkunique]
111111
[--help]
112112

113113
pg_probackup show -B backup-path

‎tests/expected/option_version.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pg_probackup 2.5.4
1+
pg_probackup 2.5.5

0 commit comments

Comments
 (0)
Please sign in to comment.