@@ -24,6 +24,9 @@ import (
24
24
"time"
25
25
26
26
"github.com/pingcap/errors"
27
+ "github.com/pingcap/failpoint"
28
+ "github.com/pingcap/tidb/pkg/ddl"
29
+ "github.com/pingcap/tidb/pkg/ddl/util/callback"
27
30
"github.com/pingcap/tidb/pkg/domain"
28
31
mysql "github.com/pingcap/tidb/pkg/errno"
29
32
"github.com/pingcap/tidb/pkg/executor"
@@ -34,6 +37,7 @@ import (
34
37
"github.com/pingcap/tidb/pkg/sessionctx/variable"
35
38
"github.com/pingcap/tidb/pkg/table"
36
39
"github.com/pingcap/tidb/pkg/table/tables"
40
+ "github.com/pingcap/tidb/pkg/tablecodec"
37
41
"github.com/pingcap/tidb/pkg/testkit"
38
42
"github.com/pingcap/tidb/pkg/testkit/testutil"
39
43
"github.com/pingcap/tidb/pkg/types"
@@ -42,6 +46,7 @@ import (
42
46
"github.com/pingcap/tidb/pkg/util/logutil/consistency"
43
47
"github.com/pingcap/tidb/pkg/util/mock"
44
48
"github.com/pingcap/tidb/pkg/util/redact"
49
+ "github.com/stretchr/testify/assert"
45
50
"github.com/stretchr/testify/require"
46
51
"go.uber.org/zap"
47
52
)
@@ -1773,3 +1778,237 @@ func TestAdminCheckTableErrorLocateForClusterIndex(t *testing.T) {
1773
1778
tk .MustExec ("admin check table admin_test" )
1774
1779
}
1775
1780
}
1781
+
1782
+ func TestAdminCheckGlobalIndex (t * testing.T ) {
1783
+ store , domain := testkit .CreateMockStoreAndDomain (t )
1784
+
1785
+ tk := testkit .NewTestKit (t , store )
1786
+ var enableFastCheck = []bool {false , true }
1787
+ for _ , enabled := range enableFastCheck {
1788
+ tk .MustExec ("use test" )
1789
+ tk .MustExec ("drop table if exists admin_test" )
1790
+
1791
+ tk .MustExec ("set tidb_enable_global_index = true" )
1792
+ tk .MustExec (fmt .Sprintf ("set tidb_enable_fast_table_check = %v" , enabled ))
1793
+
1794
+ tk .MustExec ("create table admin_test (a int, b int, c int, unique key uidx_a(a)) partition by hash(c) partitions 5" )
1795
+ tk .MustExec ("insert admin_test values (-10, -20, 1), (-1, -10, 2), (1, 11, 3), (2, 12, 0), (5, 15, -1), (10, 20, -2), (20, 30, -3)" )
1796
+
1797
+ // Make some corrupted index. Build the index information.
1798
+ sctx := mock .NewContext ()
1799
+ sctx .Store = store
1800
+ is := domain .InfoSchema ()
1801
+ dbName := model .NewCIStr ("test" )
1802
+ tblName := model .NewCIStr ("admin_test" )
1803
+ tbl , err := is .TableByName (dbName , tblName )
1804
+ require .NoError (t , err )
1805
+ tblInfo := tbl .Meta ()
1806
+ idxInfo := tblInfo .Indices [0 ]
1807
+ require .True (t , idxInfo .Global )
1808
+ idx := tbl .Indices ()[0 ]
1809
+ require .NotNil (t , idx )
1810
+
1811
+ // Reduce one row of table.
1812
+ // Index count > table count, (2, 12, 0) is deleted.
1813
+ txn , err := store .Begin ()
1814
+ require .NoError (t , err )
1815
+ err = txn .Delete (tablecodec .EncodeRowKey (tblInfo .GetPartitionInfo ().Definitions [0 ].ID , kv .IntHandle (4 ).Encoded ()))
1816
+ require .NoError (t , err )
1817
+ err = txn .Commit (context .Background ())
1818
+ require .NoError (t , err )
1819
+ err = tk .ExecToErr ("admin check table admin_test" )
1820
+ require .Error (t , err )
1821
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1822
+ require .ErrorContains (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 4, index-values:\" handle: 4, values: [KindInt64 2" )
1823
+
1824
+ indexOpr := tables .NewIndex (tblInfo .GetPartitionInfo ().Definitions [0 ].ID , tblInfo , idxInfo )
1825
+ // Remove corresponding index key/value.
1826
+ // Admin check table will success.
1827
+ txn , err = store .Begin ()
1828
+ require .NoError (t , err )
1829
+ err = indexOpr .Delete (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (2 )}, kv .IntHandle (4 ))
1830
+ require .NoError (t , err )
1831
+ err = txn .Commit (context .Background ())
1832
+ require .NoError (t , err )
1833
+ tk .MustExec ("admin check table admin_test" )
1834
+
1835
+ indexOpr = tables .NewIndex (tblInfo .GetPartitionInfo ().Definitions [2 ].ID , tblInfo , idxInfo )
1836
+
1837
+ // Reduce one row of index.
1838
+ // Index count < table count, (-1, -10, 2) is deleted.
1839
+ txn , err = store .Begin ()
1840
+ require .NoError (t , err )
1841
+ err = indexOpr .Delete (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (- 1 )}, kv .IntHandle (2 ))
1842
+ require .NoError (t , err )
1843
+ err = txn .Commit (context .Background ())
1844
+ require .NoError (t , err )
1845
+ err = tk .ExecToErr ("admin check table admin_test" )
1846
+ require .Error (t , err )
1847
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1848
+ require .EqualError (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 2, index-values:\" \" != record-values:\" handle: 2, values: [KindInt64 -1]\" " )
1849
+
1850
+ // Add one row of index with inconsistent value.
1851
+ // Index count = table count, but data is different.
1852
+ txn , err = store .Begin ()
1853
+ require .NoError (t , err )
1854
+ _ , err = indexOpr .Create (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (100 )}, kv .IntHandle (2 ), nil )
1855
+ require .NoError (t , err )
1856
+ err = txn .Commit (context .Background ())
1857
+ require .NoError (t , err )
1858
+ err = tk .ExecToErr ("admin check table admin_test" )
1859
+ require .Error (t , err )
1860
+ if ! enabled {
1861
+ require .True (t , consistency .ErrAdminCheckInconsistentWithColInfo .Equal (err ))
1862
+ require .EqualError (t , err , "[executor:8134]data inconsistency in table: admin_test, index: uidx_a, col: a, handle: \" 2\" , index-values:\" KindInt64 100\" != record-values:\" KindInt64 -1\" , compare err:<nil>" )
1863
+ } else {
1864
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1865
+ require .EqualError (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 2, index-values:\" handle: 2, values: [KindInt64 100]\" != record-values:\" handle: 2, values: [KindInt64 -1]\" " )
1866
+ }
1867
+ }
1868
+ }
1869
+
1870
+ func TestAdminCheckGlobalIndexWithClusterIndex (t * testing.T ) {
1871
+ store , domain := testkit .CreateMockStoreAndDomain (t )
1872
+
1873
+ tk := testkit .NewTestKit (t , store )
1874
+
1875
+ getCommonHandle := func (row int ) * kv.CommonHandle {
1876
+ h , err := codec .EncodeKey (tk .Session ().GetSessionVars ().StmtCtx .TimeZone (), nil , types .MakeDatums (row )... )
1877
+ require .NoError (t , err )
1878
+ ch , err := kv .NewCommonHandle (h )
1879
+ require .NoError (t , err )
1880
+ return ch
1881
+ }
1882
+
1883
+ var enableFastCheck = []bool {false , true }
1884
+ for _ , enabled := range enableFastCheck {
1885
+ tk .MustExec ("use test" )
1886
+ tk .MustExec ("drop table if exists admin_test" )
1887
+
1888
+ tk .MustExec ("set tidb_enable_global_index = true" )
1889
+ tk .MustExec (fmt .Sprintf ("set tidb_enable_fast_table_check = %v" , enabled ))
1890
+
1891
+ tk .MustExec ("create table admin_test (a int, b int, c int, unique key uidx_a(a), primary key(c)) partition by hash(c) partitions 5" )
1892
+ tk .MustExec ("insert admin_test values (-10, -20, 1), (-1, -10, 2), (1, 11, 3), (2, 12, 0), (5, 15, -1), (10, 20, -2), (20, 30, -3)" )
1893
+
1894
+ // Make some corrupted index. Build the index information.
1895
+ sctx := mock .NewContext ()
1896
+ sctx .Store = store
1897
+ is := domain .InfoSchema ()
1898
+ dbName := model .NewCIStr ("test" )
1899
+ tblName := model .NewCIStr ("admin_test" )
1900
+ tbl , err := is .TableByName (dbName , tblName )
1901
+ require .NoError (t , err )
1902
+ tblInfo := tbl .Meta ()
1903
+ idxInfo := tblInfo .Indices [0 ]
1904
+ require .True (t , idxInfo .Global )
1905
+ df := tblInfo .GetPartitionInfo ().Definitions [0 ]
1906
+
1907
+ // Reduce one row of table.
1908
+ // Index count > table count, (2, 12, 0) is deleted.
1909
+ txn , err := store .Begin ()
1910
+ require .NoError (t , err )
1911
+ txn .Delete (tablecodec .EncodeRowKey (df .ID , kv .IntHandle (0 ).Encoded ()))
1912
+ err = txn .Commit (context .Background ())
1913
+ require .NoError (t , err )
1914
+ err = tk .ExecToErr ("admin check table admin_test" )
1915
+ require .Error (t , err )
1916
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1917
+ require .ErrorContains (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 0, index-values:\" handle: 0, values: [KindInt64 2" )
1918
+
1919
+ indexOpr := tables .NewIndex (tblInfo .GetPartitionInfo ().Definitions [0 ].ID , tblInfo , idxInfo )
1920
+ // Remove corresponding index key/value.
1921
+ // Admin check table will success.
1922
+ txn , err = store .Begin ()
1923
+ require .NoError (t , err )
1924
+ err = indexOpr .Delete (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (2 )}, getCommonHandle (0 ))
1925
+ require .NoError (t , err )
1926
+ err = txn .Commit (context .Background ())
1927
+ require .NoError (t , err )
1928
+ tk .MustExec ("admin check table admin_test" )
1929
+
1930
+ indexOpr = tables .NewIndex (tblInfo .GetPartitionInfo ().Definitions [2 ].ID , tblInfo , idxInfo )
1931
+ // Reduce one row of index.
1932
+ // Index count < table count, (-1, -10, 2) is deleted.
1933
+ txn , err = store .Begin ()
1934
+ require .NoError (t , err )
1935
+ err = indexOpr .Delete (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (- 1 )}, getCommonHandle (2 ))
1936
+ require .NoError (t , err )
1937
+ err = txn .Commit (context .Background ())
1938
+ require .NoError (t , err )
1939
+ err = tk .ExecToErr ("admin check table admin_test" )
1940
+ require .Error (t , err )
1941
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1942
+ require .EqualError (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 2, index-values:\" \" != record-values:\" handle: 2, values: [KindInt64 -1]\" " )
1943
+
1944
+ // Add one row with inconsistent value.
1945
+ // Index count = table count, but data is different.
1946
+ txn , err = store .Begin ()
1947
+ require .NoError (t , err )
1948
+ _ , err = indexOpr .Create (tk .Session ().GetTableCtx (), txn , []types.Datum {types .NewIntDatum (100 )}, getCommonHandle (2 ), nil )
1949
+ require .NoError (t , err )
1950
+ err = txn .Commit (context .Background ())
1951
+ require .NoError (t , err )
1952
+ err = tk .ExecToErr ("admin check table admin_test" )
1953
+ require .Error (t , err )
1954
+ if ! enabled {
1955
+ require .True (t , consistency .ErrAdminCheckInconsistentWithColInfo .Equal (err ))
1956
+ require .EqualError (t , err , "[executor:8134]data inconsistency in table: admin_test, index: uidx_a, col: a, handle: \" 2\" , index-values:\" KindInt64 100\" != record-values:\" KindInt64 -1\" , compare err:<nil>" )
1957
+ } else {
1958
+ require .True (t , consistency .ErrAdminCheckInconsistent .Equal (err ))
1959
+ require .EqualError (t , err , "[admin:8223]data inconsistency in table: admin_test, index: uidx_a, handle: 2, index-values:\" handle: 2, values: [KindInt64 100]\" != record-values:\" handle: 2, values: [KindInt64 -1]\" " )
1960
+ }
1961
+ }
1962
+ }
1963
+
1964
+ func TestAdminCheckGlobalIndexDuringDDL (t * testing.T ) {
1965
+ store , dom := testkit .CreateMockStoreAndDomain (t )
1966
+ originalHook := dom .DDL ().GetHook ()
1967
+ tk := testkit .NewTestKit (t , store )
1968
+
1969
+ var schemaMap = make (map [model.SchemaState ]struct {})
1970
+
1971
+ hook := & callback.TestDDLCallback {Do : dom }
1972
+ onJobUpdatedExportedFunc := func (job * model.Job ) {
1973
+ schemaMap [job .SchemaState ] = struct {}{}
1974
+ _ , err := tk .Exec ("admin check table admin_test" )
1975
+ assert .NoError (t , err )
1976
+ }
1977
+ hook .OnJobUpdatedExported .Store (& onJobUpdatedExportedFunc )
1978
+
1979
+ // check table after delete some index key/value pairs.
1980
+ ddl .MockDMLExecution = func () {
1981
+ _ , err := tk .Exec ("admin check table admin_test" )
1982
+ assert .NoError (t , err )
1983
+ }
1984
+
1985
+ batchSize := 32
1986
+ tk .MustExec (fmt .Sprintf ("set global tidb_ddl_reorg_batch_size = %d" , batchSize ))
1987
+
1988
+ var enableFastCheck = []bool {false , true }
1989
+ for _ , enabled := range enableFastCheck {
1990
+ tk .MustExec ("use test" )
1991
+ tk .MustExec ("drop table if exists admin_test" )
1992
+
1993
+ tk .MustExec ("set tidb_enable_global_index = true" )
1994
+ tk .MustExec (fmt .Sprintf ("set tidb_enable_fast_table_check = %v" , enabled ))
1995
+
1996
+ tk .MustExec ("create table admin_test (a int, b int, c int, unique key uidx_a(a), primary key(c)) partition by hash(c) partitions 5" )
1997
+ tk .MustExec ("insert admin_test values (-10, -20, 1), (-1, -10, 2), (1, 11, 3), (2, 12, 0), (5, 15, -1), (10, 20, -2), (20, 30, -3)" )
1998
+ for i := 1 ; i <= batchSize * 2 ; i ++ {
1999
+ tk .MustExec (fmt .Sprintf ("insert admin_test values (%d, %d, %d)" , i * 5 + 1 , i , i * 5 + 1 ))
2000
+ }
2001
+
2002
+ dom .DDL ().SetHook (hook )
2003
+ require .NoError (t , failpoint .Enable ("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution" , "1*return(true)->return(false)" ))
2004
+ tk .MustExec ("alter table admin_test truncate partition p1" )
2005
+ require .NoError (t , failpoint .Disable ("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution" ))
2006
+ dom .DDL ().SetHook (originalHook )
2007
+
2008
+ // Should have 3 different schema states, `none`, `deleteOnly`, `deleteReorg`
2009
+ require .Len (t , schemaMap , 3 )
2010
+ for ss := range schemaMap {
2011
+ delete (schemaMap , ss )
2012
+ }
2013
+ }
2014
+ }
0 commit comments