@@ -455,6 +455,83 @@ func TestSnapshotAnalyzeAndMaxTSAnalyze(t *testing.T) {
455455 }
456456}
457457
458+ func TestAnalyzeUsesNDVRateOption (t * testing.T ) {
459+ store , dom := testkit .CreateMockStoreAndDomain (t )
460+ tk := testkit .NewTestKit (t , store )
461+
462+ tk .MustExec ("use test" )
463+ tk .MustExec ("set @@tidb_analyze_version = 2" )
464+ tk .MustExec ("create table t_ndv_rate(a int, b int, index idx_b(b))" )
465+ // 200 rows: col a is fully unique; col b has 10 distinct values, each repeated 20 times.
466+ values := make ([]string , 0 , 200 )
467+ for i := 1 ; i <= 200 ; i ++ {
468+ values = append (values , fmt .Sprintf ("(%d,%d)" , i , i % 10 ))
469+ }
470+ tk .MustExec ("insert into t_ndv_rate values " + strings .Join (values , "," ))
471+
472+ tbl , err := dom .InfoSchema ().TableByName (context .Background (), ast .NewCIStr ("test" ), ast .NewCIStr ("t_ndv_rate" ))
473+ require .NoError (t , err )
474+ tblInfo := tbl .Meta ()
475+ readNDV := func (histID int64 , isIndex int ) int64 {
476+ rows := tk .MustQuery (fmt .Sprintf (
477+ "select distinct_count from mysql.stats_histograms where table_id = %d and hist_id = %d and is_index = %d" ,
478+ tblInfo .ID ,
479+ histID ,
480+ isIndex ,
481+ )).Rows ()
482+ require .Len (t , rows , 1 )
483+ n , err := strconv .ParseInt (rows [0 ][0 ].(string ), 10 , 64 )
484+ require .NoError (t , err )
485+ return n
486+ }
487+
488+ t .Run ("exact path with ndvrate=1" , func (t * testing.T ) {
489+ tk .MustExec ("analyze table t_ndv_rate with 1 samplerate, 1 ndvrate" )
490+ require .Equal (t , int64 (200 ), readNDV (tblInfo .Columns [0 ].ID , 0 ))
491+ require .Equal (t , int64 (10 ), readNDV (tblInfo .Columns [1 ].ID , 0 ))
492+ require .Equal (t , int64 (10 ), readNDV (tblInfo .Indices [0 ].ID , 1 ))
493+ })
494+
495+ t .Run ("singleton-sketch path with ndvrate<1" , func (t * testing.T ) {
496+ // Pin the unistore analyze RNG seed so the Bernoulli sketch sampling and the
497+ // resulting GEE estimate are deterministic.
498+ require .NoError (t , failpoint .Enable ("github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler/mockAnalyzeSamplingSeed" , "return(1)" ))
499+ defer func () {
500+ require .NoError (t , failpoint .Disable ("github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler/mockAnalyzeSamplingSeed" ))
501+ }()
502+ tk .MustExec ("analyze table t_ndv_rate with 0.5 samplerate, 0.5 ndvrate" )
503+ // True NDVs are 200/10/10. Column `a` lands at 141 (instead of 200) because
504+ // GEE under-estimates NDV on fully-unique columns from a half-sample; this
505+ // confirms the singleton-sketch path is exercised rather than the exact one.
506+ require .Equal (t , int64 (141 ), readNDV (tblInfo .Columns [0 ].ID , 0 ))
507+ require .Equal (t , int64 (10 ), readNDV (tblInfo .Columns [1 ].ID , 0 ))
508+ require .Equal (t , int64 (10 ), readNDV (tblInfo .Indices [0 ].ID , 1 ))
509+ })
510+ }
511+
512+ func TestAnalyzeRaisesNDVRateNote (t * testing.T ) {
513+ store , dom := testkit .CreateMockStoreAndDomain (t )
514+ tk := testkit .NewTestKit (t , store )
515+
516+ tk .MustExec ("use test" )
517+ tk .MustExec ("set @@tidb_analyze_version = 2" )
518+ tk .MustExec ("create table t_ndv_note(a int)" )
519+ tk .MustExec ("insert into t_ndv_note values (1),(2),(3)" )
520+
521+ // Force stats_meta to a tiny count so getAdjustedSampleRate returns 1, which
522+ // will exceed the user's configured ndvrate of 0.5 and trigger the clamp.
523+ statsHandle := dom .StatsHandle ()
524+ tbl , err := dom .InfoSchema ().TableByName (context .Background (), ast .NewCIStr ("test" ), ast .NewCIStr ("t_ndv_note" ))
525+ require .NoError (t , err )
526+ tk .MustExec (fmt .Sprintf ("update mysql.stats_meta set count = 3 where table_id = %d" , tbl .Meta ().ID ))
527+ require .NoError (t , statsHandle .Update (context .Background (), dom .InfoSchema ()))
528+
529+ tk .MustExec ("analyze table t_ndv_note with 0.5 ndvrate" )
530+ tk .MustQuery ("show warnings" ).CheckContain (
531+ "Analyze raised NDV sample rate from 0.500000 to 1.000000 to match the auto-adjusted row sample rate for table test.t_ndv_note" ,
532+ )
533+ }
534+
458535func TestAdjustSampleRateNote (t * testing.T ) {
459536 store := testkit .CreateMockStore (t )
460537 tk := testkit .NewTestKit (t , store )
0 commit comments