@@ -211,6 +211,7 @@ def test_checkdb_amcheck_only_sanity(self):
211
211
212
212
# Clean after yourself
213
213
gdb .kill ()
214
+ node .stop ()
214
215
self .del_test_dir (module_name , fname )
215
216
216
217
# @unittest.skip("skip")
@@ -349,6 +350,7 @@ def test_basic_checkdb_amcheck_only_sanity(self):
349
350
log_file_content )
350
351
351
352
# Clean after yourself
353
+ node .stop ()
352
354
self .del_test_dir (module_name , fname )
353
355
354
356
# @unittest.skip("skip")
@@ -445,6 +447,98 @@ def test_checkdb_block_validation_sanity(self):
445
447
e .message )
446
448
447
449
# 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 ()
448
542
self .del_test_dir (module_name , fname )
449
543
450
544
# @unittest.skip("skip")
@@ -502,6 +596,7 @@ def test_checkdb_sigint_handling(self):
502
596
503
597
# Clean after yourself
504
598
gdb .kill ()
599
+ node .stop ()
505
600
self .del_test_dir (module_name , fname )
506
601
507
602
# @unittest.skip("skip")
@@ -563,12 +658,15 @@ def test_checkdb_with_least_privileges(self):
563
658
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
564
659
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
565
660
'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; '
566
662
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
567
663
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
568
664
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
569
665
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
570
666
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
571
667
'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; '
572
670
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;' # amcheck-next function
573
671
)
574
672
# PG 9.6
@@ -588,13 +686,16 @@ def test_checkdb_with_least_privileges(self):
588
686
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
589
687
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
590
688
'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; '
591
690
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
592
691
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
593
692
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
594
693
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
595
694
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
596
695
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
597
696
'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; '
598
699
# 'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup; '
599
700
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;'
600
701
)
@@ -615,13 +716,16 @@ def test_checkdb_with_least_privileges(self):
615
716
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
616
717
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
617
718
'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; '
618
720
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
619
721
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
620
722
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
621
723
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
622
724
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
623
725
'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;'
625
729
)
626
730
if ProbackupTest .enterprise :
627
731
# amcheck-1.1
@@ -633,7 +737,45 @@ def test_checkdb_with_least_privileges(self):
633
737
node .safe_psql (
634
738
'backupdb' ,
635
739
'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
637
779
else :
638
780
node .safe_psql (
639
781
'backupdb' ,
@@ -650,16 +792,24 @@ def test_checkdb_with_least_privileges(self):
650
792
'GRANT SELECT ON TABLE pg_catalog.pg_namespace TO backup; '
651
793
'GRANT EXECUTE ON FUNCTION pg_catalog.current_setting(text) TO backup; '
652
794
'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; '
653
796
'GRANT EXECUTE ON FUNCTION pg_catalog.nameeq(name, name) TO backup; '
654
797
'GRANT EXECUTE ON FUNCTION pg_catalog.namene(name, name) TO backup; '
655
798
'GRANT EXECUTE ON FUNCTION pg_catalog.int8(integer) TO backup; '
656
799
'GRANT EXECUTE ON FUNCTION pg_catalog.oideq(oid, oid) TO backup; '
657
800
'GRANT EXECUTE ON FUNCTION pg_catalog.charne("char", "char") TO backup; '
658
801
'GRANT EXECUTE ON FUNCTION pg_catalog.pg_is_in_recovery() TO backup; '
659
802
'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; '
660
805
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO backup; '
661
806
'GRANT EXECUTE ON FUNCTION bt_index_check(regclass, bool) TO backup;'
662
807
)
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" )
663
813
664
814
if ProbackupTest .enterprise :
665
815
node .safe_psql (
@@ -700,4 +850,5 @@ def test_checkdb_with_least_privileges(self):
700
850
repr (e .message ), self .cmd ))
701
851
702
852
# Clean after yourself
853
+ node .stop ()
703
854
self .del_test_dir (module_name , fname )
0 commit comments