11package sqlancer ;
22
3+ import java .time .Duration ;
4+ import java .time .Instant ;
35import java .util .ArrayList ;
46import java .util .List ;
5- import java .util .function .BiFunction ;
6- import java .util .stream .Collectors ;
77
88import sqlancer .common .query .Query ;
99
1010public class StatementReducer <G extends GlobalState <O , ?, C >, O extends DBMSSpecificOptions <?>, C extends SQLancerDBConnection >
1111 implements Reducer <G > {
1212 private final DatabaseProvider <G , O , C > provider ;
1313 private boolean observedChange ;
14+ private int partitionNum ;
1415
1516 public StatementReducer (DatabaseProvider <G , O , C > provider ) {
1617 this .provider = provider ;
1718 }
1819
20+ private boolean hasNotReachedLimit (long curr , long limit ) {
21+ if (limit == MainOptions .NO_REDUCE_LIMIT ) {
22+ return true ;
23+ }
24+ return curr < limit ;
25+ }
26+
1927 @ SuppressWarnings ("unchecked" )
2028 @ Override
2129 public void reduce (G state , Reproducer <G > reproducer , G newGlobalState ) throws Exception {
2230
31+ long maxReduceTime = state .getOptions ().getMaxStatementReduceTime ();
32+ long maxReduceSteps = state .getOptions ().getMaxStatementReduceSteps ();
33+
2334 List <Query <C >> knownToReproduceBugStatements = new ArrayList <>();
2435 for (Query <?> stat : state .getState ().getStatements ()) {
2536 knownToReproduceBugStatements .add ((Query <C >) stat );
2637 }
38+
2739 System .out .println ("Starting query:" );
2840 printQueries (knownToReproduceBugStatements );
2941 System .out .println ();
3042
31- do {
43+ if (knownToReproduceBugStatements .size () <= 1 ) {
44+ return ;
45+ }
46+
47+ Instant timeOfReductionBegins = Instant .now ();
48+ long currentReduceSteps = 0 ;
49+ long currentReduceTime = 0 ;
50+ partitionNum = 2 ;
51+
52+ while (knownToReproduceBugStatements .size () >= 2 && hasNotReachedLimit (currentReduceSteps , maxReduceSteps )
53+ && hasNotReachedLimit (currentReduceTime , maxReduceTime )) {
3254 observedChange = false ;
55+
3356 knownToReproduceBugStatements = tryReduction (state , reproducer , newGlobalState ,
34- knownToReproduceBugStatements , (candidateStatements , i ) -> {
35- candidateStatements .remove ((int ) i );
36- return true ;
37- });
38- } while (observedChange );
57+ knownToReproduceBugStatements );
58+ if (!observedChange ) {
59+ if (partitionNum == knownToReproduceBugStatements .size ()) {
60+ break ;
61+ }
62+ // increase the search granularity
63+ partitionNum = Math .min (partitionNum * 2 , knownToReproduceBugStatements .size ());
64+
65+ currentReduceSteps ++;
66+ Instant currentInstant = Instant .now ();
67+ currentReduceTime = Duration .between (currentInstant , timeOfReductionBegins ).getSeconds ();
68+ }
69+
70+ }
3971
4072 System .out .println ("Reduced query:" );
4173 printQueries (knownToReproduceBugStatements );
74+ newGlobalState .getState ().setStatements (new ArrayList <>(knownToReproduceBugStatements ));
4275 }
4376
4477 private List <Query <C >> tryReduction (G state , // NOPMD
45- Reproducer <G > reproducer , G newGlobalState , List <Query <C >> knownToReproduceBugStatements ,
46- BiFunction <List <Query <C >>, Integer , Boolean > reductionOperation ) throws Exception {
78+ Reproducer <G > reproducer , G newGlobalState , List <Query <C >> knownToReproduceBugStatements ) throws Exception {
4779
4880 List <Query <C >> statements = knownToReproduceBugStatements ;
49- for (int i = 0 ; i < statements .size (); i ++) {
81+
82+ int start = 0 ;
83+ int subLength = statements .size () / partitionNum ;
84+ while (start < statements .size ()) {
85+ // newStatements = candidate[:start] + candidate[start+subLength:]
86+ // in other word, remove [start, start+subLength) from candidates
5087 try (C con2 = provider .createDatabase (newGlobalState )) {
5188 newGlobalState .setConnection (con2 );
5289 List <Query <C >> candidateStatements = new ArrayList <>(statements );
53- if (!reductionOperation .apply (candidateStatements , i )) {
54- continue ;
55- }
56- newGlobalState .getState ().setStatements (candidateStatements .stream ().collect (Collectors .toList ()));
90+ candidateStatements .subList (start , start + subLength ).clear ();
91+ newGlobalState .getState ().setStatements (new ArrayList <>(candidateStatements ));
92+
5793 for (Query <C > s : candidateStatements ) {
5894 try {
5995 s .execute (newGlobalState );
@@ -65,13 +101,16 @@ private List<Query<C>> tryReduction(G state, // NOPMD
65101 if (reproducer .bugStillTriggers (newGlobalState )) {
66102 observedChange = true ;
67103 statements = candidateStatements ;
104+ partitionNum = Math .max (partitionNum - 1 , 2 );
105+ break ;
68106 // reproducer.outputHook((SQLite3GlobalState) newGlobalState);
69107 // state.getLogger().logReduced(newGlobalState.getState());
70108 }
71109 } catch (Throwable ignoredException ) {
72110
73111 }
74112 }
113+ start = start + subLength ;
75114 }
76115 return statements ;
77116 }
0 commit comments