@@ -59,7 +59,7 @@ func TestHintCacheBasic(t *testing.T) {
5959 // Query for hints on a statement that has no hints.
6060 nonHintedFingerprint := "SELECT x FROM t WHERE y = $1"
6161 require .False (t , hc .TestingHashHasHints (computeHash (t , nonHintedFingerprint )))
62- require . Nil (t , hc . MaybeGetStatementHints ( ctx , nonHintedFingerprint ) )
62+ requireHintsCount (t , hc , ctx , nonHintedFingerprint , 0 )
6363
6464 // Add a hint for another statement.
6565 fingerprint2 := "SELECT c FROM t WHERE d = $1"
@@ -122,43 +122,43 @@ func TestHintCacheLRU(t *testing.T) {
122122
123123 // Access the first two fingerprints to populate the cache.
124124 // This should result in 2 table reads.
125- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [0 ]) )
125+ requireHintsCount (t , hc , ctx , fingerprints [0 ], 1 )
126126 require .Equal (t , 1 , hc .TestingNumTableReads ())
127127
128- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [1 ]) )
128+ requireHintsCount (t , hc , ctx , fingerprints [1 ], 1 )
129129 require .Equal (t , 2 , hc .TestingNumTableReads ())
130130
131131 // Access the same fingerprints again - should be served from cache with no
132132 // additional reads.
133- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [0 ]) )
134- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [1 ]) )
133+ requireHintsCount (t , hc , ctx , fingerprints [0 ], 1 )
134+ requireHintsCount (t , hc , ctx , fingerprints [1 ], 1 )
135135 require .Equal (t , 2 , hc .TestingNumTableReads ())
136136
137137 // Access the third fingerprint. This should evict the first (LRU) due to
138138 // cache size limit of 2, resulting in one more table read.
139- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [2 ]) )
139+ requireHintsCount (t , hc , ctx , fingerprints [2 ], 1 )
140140 require .Equal (t , 3 , hc .TestingNumTableReads ())
141141
142142 // Access the first fingerprint again. Since it was evicted, this should
143143 // result in another table read on the first access.
144- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [0 ]) )
144+ requireHintsCount (t , hc , ctx , fingerprints [0 ], 1 )
145145 require .Equal (t , 4 , hc .TestingNumTableReads ())
146146
147147 // Access the second fingerprint. It should have been evicted by now, so
148148 // another table read on the first access.
149- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [1 ]) )
149+ requireHintsCount (t , hc , ctx , fingerprints [1 ], 1 )
150150 require .Equal (t , 5 , hc .TestingNumTableReads ())
151151
152152 // The first and second fingerprint should now be cached, so accessing them
153153 // again should not increase table reads.
154- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [0 ]) )
155- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [1 ]) )
154+ requireHintsCount (t , hc , ctx , fingerprints [0 ], 1 )
155+ requireHintsCount (t , hc , ctx , fingerprints [1 ], 1 )
156156 require .Equal (t , 5 , hc .TestingNumTableReads ())
157157
158158 // Access the third fingerprint again - should have been evicted, so the first
159159 // access should cause a table read.
160- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [2 ]) )
161- require . NotNil (t , hc . MaybeGetStatementHints ( ctx , fingerprints [2 ]) )
160+ requireHintsCount (t , hc , ctx , fingerprints [2 ], 1 )
161+ requireHintsCount (t , hc , ctx , fingerprints [2 ], 1 )
162162 require .Equal (t , 6 , hc .TestingNumTableReads ())
163163}
164164
@@ -204,15 +204,15 @@ func TestHintCacheInitialScan(t *testing.T) {
204204 require .True (t , hc .TestingHashHasHints (hashes [0 ]))
205205 require .True (t , hc .TestingHashHasHints (hashes [1 ]))
206206 require .True (t , hc .TestingHashHasHints (hashes [2 ]))
207- require . Len (t , hc . MaybeGetStatementHints ( ctx , fingerprints [0 ]) , 2 )
208- require . Len (t , hc . MaybeGetStatementHints ( ctx , fingerprints [1 ]) , 1 )
209- require . Len (t , hc . MaybeGetStatementHints ( ctx , fingerprints [2 ]) , 1 )
207+ requireHintsCount (t , hc , ctx , fingerprints [0 ], 2 )
208+ requireHintsCount (t , hc , ctx , fingerprints [1 ], 1 )
209+ requireHintsCount (t , hc , ctx , fingerprints [2 ], 1 )
210210
211211 // Query for a fingerprint with no hints.
212212 nonHintedFingerprint := "SELECT x FROM t WHERE y = $1"
213213 nonHintedHash := computeHash (t , nonHintedFingerprint )
214214 require .False (t , hc .TestingHashHasHints (nonHintedHash ))
215- require . Nil (t , hc . MaybeGetStatementHints ( ctx , nonHintedFingerprint ) )
215+ requireHintsCount (t , hc , ctx , nonHintedFingerprint , 0 )
216216
217217 // After the initial scan, new hints should still be detected via rangefeed.
218218 fingerprint4 := "SELECT g FROM t WHERE h = $1"
@@ -274,8 +274,8 @@ func TestHintCacheMultiNode(t *testing.T) {
274274 require .Equal (t , 1 , hc .TestingHashCount ())
275275
276276 // Verify that fingerprint1 still has hints but fingerprint2 doesn't.
277- require . Len (t , hc . MaybeGetStatementHints ( ctx , fingerprint1 ) , 1 )
278- require . Nil (t , hc . MaybeGetStatementHints ( ctx , fingerprint2 ) )
277+ requireHintsCount (t , hc , ctx , fingerprint1 , 1 )
278+ requireHintsCount (t , hc , ctx , fingerprint2 , 0 )
279279}
280280
281281// TestHintCacheMultiTenant tests that hint caches for different tenants are
@@ -317,24 +317,24 @@ func TestHintCacheMultiTenant(t *testing.T) {
317317 insertStatementHint (t , r1 , fingerprint1 )
318318 waitForUpdateOnFingerprintHash (t , ctx , hc1 , fingerprint1 , 1 )
319319 require .Equal (t , 1 , hc1 .TestingHashCount ())
320- require . Len (t , hc1 . MaybeGetStatementHints ( ctx , fingerprint1 ) , 1 )
320+ requireHintsCount (t , hc1 , ctx , fingerprint1 , 1 )
321321
322322 // Tenant2's cache should remain empty.
323323 require .Equal (t , 0 , hc2 .TestingHashCount ())
324324 require .False (t , hc2 .TestingHashHasHints (computeHash (t , fingerprint1 )))
325- require . Nil (t , hc2 . MaybeGetStatementHints ( ctx , fingerprint1 ) )
325+ requireHintsCount (t , hc2 , ctx , fingerprint1 , 0 )
326326
327327 // Insert a different hint for tenant2.
328328 fingerprint2 := "SELECT c FROM t WHERE d = $1"
329329 insertStatementHint (t , r2 , fingerprint2 )
330330 waitForUpdateOnFingerprintHash (t , ctx , hc2 , fingerprint2 , 1 )
331331 require .Equal (t , 1 , hc2 .TestingHashCount ())
332- require . Len (t , hc2 . MaybeGetStatementHints ( ctx , fingerprint2 ) , 1 )
332+ requireHintsCount (t , hc2 , ctx , fingerprint2 , 1 )
333333
334334 // Tenant1's cache should still only have its original hint.
335335 require .Equal (t , 1 , hc1 .TestingHashCount ())
336336 require .False (t , hc1 .TestingHashHasHints (computeHash (t , fingerprint2 )))
337- require . Nil (t , hc1 . MaybeGetStatementHints ( ctx , fingerprint2 ) )
337+ requireHintsCount (t , hc1 , ctx , fingerprint2 , 0 )
338338
339339 // Insert the same fingerprint in both tenants - they should be independent.
340340 fingerprintShared := "SELECT x FROM t WHERE y = $1"
@@ -352,8 +352,8 @@ func TestHintCacheMultiTenant(t *testing.T) {
352352 // Verify that the shared fingerprint has different counts in each tenant.
353353 require .True (t , hc1 .TestingHashHasHints (computeHash (t , fingerprintShared )))
354354 require .True (t , hc2 .TestingHashHasHints (computeHash (t , fingerprintShared )))
355- require . Len (t , hc1 . MaybeGetStatementHints ( ctx , fingerprintShared ) , 2 )
356- require . Len (t , hc2 . MaybeGetStatementHints ( ctx , fingerprintShared ) , 1 )
355+ requireHintsCount (t , hc1 , ctx , fingerprintShared , 2 )
356+ requireHintsCount (t , hc2 , ctx , fingerprintShared , 1 )
357357
358358 // Delete one hint from tenant1's shared fingerprint.
359359 deleteStatementHints (t , r1 , fingerprintShared , 1 )
@@ -370,8 +370,8 @@ func TestHintCacheMultiTenant(t *testing.T) {
370370 // Tenant1 should still have the hint, but tenant2 should not.
371371 require .True (t , hc1 .TestingHashHasHints (computeHash (t , fingerprintShared )))
372372 require .False (t , hc2 .TestingHashHasHints (computeHash (t , fingerprintShared )))
373- require . Len (t , hc1 . MaybeGetStatementHints ( ctx , fingerprintShared ) , 1 )
374- require . Nil (t , hc2 . MaybeGetStatementHints ( ctx , fingerprintShared ) )
373+ requireHintsCount (t , hc1 , ctx , fingerprintShared , 1 )
374+ requireHintsCount (t , hc2 , ctx , fingerprintShared , 0 )
375375}
376376
377377// TestHintCacheGeneration tests that the cache generation is incremented
@@ -505,14 +505,39 @@ func waitForUpdateOnFingerprintHash(
505505 if hasHints := hc .TestingHashHasHints (hash ); (expected > 0 ) != hasHints {
506506 return errors .Errorf ("expected hash %d with hasHints=%t, got hasHints=%t" , hash , expected > 0 , hasHints )
507507 }
508- hints := hc .MaybeGetStatementHints (ctx , fingerprint )
508+ hints , ids := hc .MaybeGetStatementHints (ctx , fingerprint )
509509 if len (hints ) != expected {
510510 return errors .Errorf ("expected %d hints for fingerprint %q, got %d" , expected , fingerprint , len (hints ))
511511 }
512+ checkIDOrder (t , ids )
512513 return nil
513514 })
514515}
515516
517+ // requireHintsCount verifies that MaybeGetStatementHints returns the expected number of hints and IDs.
518+ func requireHintsCount (
519+ t * testing.T ,
520+ hc * hints.StatementHintsCache ,
521+ ctx context.Context ,
522+ fingerprint string ,
523+ expectedCount int ,
524+ ) {
525+ hints , ids := hc .MaybeGetStatementHints (ctx , fingerprint )
526+ require .Len (t , hints , expectedCount )
527+ require .Len (t , ids , expectedCount )
528+ checkIDOrder (t , ids )
529+ }
530+
531+ // checkIDOrder verifies that the row IDs are in ascending order.
532+ func checkIDOrder (t * testing.T , ids []int64 ) {
533+ t .Helper ()
534+ for i := 1 ; i < len (ids ); i ++ {
535+ if ids [i ] <= ids [i - 1 ] {
536+ t .Fatalf ("expected IDs to be in ascending order, got %v" , ids )
537+ }
538+ }
539+ }
540+
516541// insertStatementHint inserts an empty statement hint into the
517542// system.statement_hints table.
518543func insertStatementHint (t * testing.T , r * sqlutils.SQLRunner , fingerprint string ) {
0 commit comments