SuspendableGetRows.java

1
package com.jsql.model.suspendable;
2
3
import com.jsql.model.InjectionModel;
4
import com.jsql.model.bean.database.AbstractElementDatabase;
5
import com.jsql.model.bean.database.Table;
6
import com.jsql.view.subscriber.Seal;
7
import com.jsql.model.exception.AbstractSlidingException;
8
import com.jsql.model.exception.InjectionFailureException;
9
import com.jsql.model.exception.LoopDetectedSlidingException;
10
import com.jsql.model.exception.StoppedByUserSlidingException;
11
import com.jsql.model.injection.strategy.AbstractStrategy;
12
import com.jsql.util.LogLevelUtil;
13
import com.jsql.util.StringUtil;
14
import org.apache.commons.lang3.StringUtils;
15
import org.apache.commons.text.StringEscapeUtils;
16
import org.apache.logging.log4j.LogManager;
17
import org.apache.logging.log4j.Logger;
18
19
import java.net.URLDecoder;
20
import java.nio.charset.StandardCharsets;
21
import java.util.ArrayList;
22
import java.util.List;
23
import java.util.regex.Matcher;
24
import java.util.regex.Pattern;
25
import java.util.regex.PatternSyntaxException;
26
27
import static com.jsql.model.accessible.DataAccess.*;
28
import static com.jsql.model.injection.engine.model.EngineYaml.LIMIT;
29
30
/**
31
 * Get data as chunks by performance query from SQL request.
32
 * 
33
 * <pre>
34
 * Single row format: \4[0-9A-F]*\5[0-9A-F]*c?\4
35
 * Row separator: \6
36
 * Tape example: \4xxRow#Xxx\5x\4\6\4xxRow#X+1xx\5x\4\6...\4\1\3\3\7</pre>
37
 * 
38
 * MID and LIMIT move two sliding windows in a 2D array tape in that order.
39
 * MID skips characters when collected, then LIMIT skips lines when collected.
40
 * The process can be interrupted by the user (stop/pause).
41
 */
42
public class SuspendableGetRows extends AbstractSuspendable {
43
44
    private static final Logger LOGGER = LogManager.getRootLogger();
45
46
    public SuspendableGetRows(InjectionModel injectionModel) {
47
        super(injectionModel);
48
    }
49
50
    @Override
51
    public String run(Input input) throws AbstractSlidingException {
52
        String initialSqlQuery = input.payload();
53
        String[] sourcePage = input.sourcePage();  // value overridden, useless + not sourcepage
54
        boolean isMultipleRows = input.isMultipleRows();
55
        int countRowsToFind = input.countRowsToFind();
56
        AbstractElementDatabase elementDatabase = input.elementDatabase();
57
        String metadataInjectionProcess = input.metadataInjectionProcess();
58
        
59 1 1. run : removed call to com/jsql/util/ThreadUtil::put → NO_COVERAGE
        this.injectionModel.getMediatorUtils().threadUtil().put(elementDatabase, this);
60
61
        AbstractStrategy strategy = this.injectionModel.getMediatorStrategy().getStrategy();
62
        
63
        // Fix #14417
64 1 1. run : negated conditional → NO_COVERAGE
        if (strategy == null) {
65
            return StringUtils.EMPTY;
66
        }
67
        
68
        // Stop injection if all rows are found, skip rows and characters collected
69
        var slidingWindowAllRows = new StringBuilder();
70
        var slidingWindowCurrentRow = new StringBuilder();
71
        
72
        String previousChunk = StringUtils.EMPTY;
73
        var countAllRows = 0;
74
        var charPositionInCurrentRow = 1;
75
        var countInfiniteLoop = 0;
76
        
77
        String queryGetRows = this.getQuery(initialSqlQuery, countAllRows);
78
        
79
        while (true) {
80 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::checkSuspend → NO_COVERAGE
            this.checkSuspend(strategy, slidingWindowAllRows, slidingWindowCurrentRow);
81
            
82
            sourcePage[0] = strategy.inject(queryGetRows, Integer.toString(charPositionInCurrentRow), this, metadataInjectionProcess);
83
            // Parse all the data we have retrieved
84
            Matcher regexLeadFound = this.parseLeadFound(sourcePage[0], strategy.getPerformanceLength());
85
            Matcher regexTrailOnlyFound = this.parseTrailOnlyFound(sourcePage[0]);
86
            
87
            if (
88 3 1. run : negated conditional → NO_COVERAGE
2. run : negated conditional → NO_COVERAGE
3. run : negated conditional → NO_COVERAGE
                (!regexLeadFound.find() || regexTrailOnlyFound.find())
89
                && isMultipleRows
90 1 1. run : negated conditional → NO_COVERAGE
                && StringUtils.isNotEmpty(slidingWindowAllRows.toString())
91
            ) {
92 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                this.sendProgress(countRowsToFind, countRowsToFind, elementDatabase);
93
                break;
94
            }
95
96
            // Add the result to the data already found.
97
            // Fix #40947: OutOfMemoryError on append()
98
            // Fix #95382: IllegalArgumentException on URLDecoder.decode()
99
            try {
100
                String currentChunk = regexLeadFound.group(1);
101
                currentChunk = this.decodeUnicode(currentChunk, initialSqlQuery);
102
                currentChunk = this.decodeUrl(currentChunk);
103
104
                countInfiniteLoop = this.checkInfinite(countInfiniteLoop, previousChunk, currentChunk, slidingWindowCurrentRow, slidingWindowAllRows);
105
                
106
                previousChunk = currentChunk;
107
                slidingWindowCurrentRow.append(currentChunk);
108 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendChunk → NO_COVERAGE
                this.sendChunk(currentChunk);
109
            } catch (IllegalArgumentException | IllegalStateException | OutOfMemoryError e) {
110 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::endInjection → NO_COVERAGE
                this.endInjection(elementDatabase, e);
111
            }
112
113
            // Check how many rows we have collected from the beginning of that chunk
114
            int countChunkRows = this.getCountRows(slidingWindowCurrentRow);
115 2 1. run : Replaced integer addition with subtraction → NO_COVERAGE
2. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
            this.sendProgress(countRowsToFind, countAllRows + countChunkRows, elementDatabase);
116
117
            // End of rows detected: \1\3\3\7
118
            // => \4xxxxxxxx\500\4\6\4...\4\1\3\3\7
119 2 1. run : changed conditional boundary → NO_COVERAGE
2. run : negated conditional → NO_COVERAGE
            if (
120
                countChunkRows > 0
121 1 1. run : negated conditional → NO_COVERAGE
                || slidingWindowCurrentRow.toString().matches("(?s).*"+ TRAIL_RGX +".*")
122
            ) {
123 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrapeTrailJunk → NO_COVERAGE
                this.scrapeTrailJunk(slidingWindowCurrentRow);
124
                slidingWindowAllRows.append(slidingWindowCurrentRow);
125
                
126 1 1. run : negated conditional → NO_COVERAGE
                if (isMultipleRows) {
127 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE
                    this.scrap(slidingWindowAllRows);
128 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE
                    this.scrap(slidingWindowCurrentRow);
129 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::appendRowFixed → NO_COVERAGE
                    this.appendRowFixed(slidingWindowAllRows, slidingWindowCurrentRow);
130
131
                    countAllRows = this.getCountRows(slidingWindowAllRows);
132 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                    this.sendProgress(countRowsToFind, countAllRows, elementDatabase);
133
134
                    // Ending condition: every expected rows have been retrieved.
135 1 1. run : negated conditional → NO_COVERAGE
                    if (countAllRows == countRowsToFind) {
136
                        break;
137
                    }
138
                    // Add the LIMIT statement to the next SQL query and reset variables.
139
                    // Put the character cursor to the beginning of the line, and reset the result of the current query
140
                    queryGetRows = this.getQuery(initialSqlQuery, countAllRows);
141 1 1. run : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
                    slidingWindowCurrentRow.setLength(0);
142
                } else {
143 1 1. run : removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE
                    this.sendProgress(countRowsToFind, countRowsToFind, elementDatabase);
144
                    break;
145
                }
146
            }
147 1 1. run : Replaced integer addition with subtraction → NO_COVERAGE
            charPositionInCurrentRow = slidingWindowCurrentRow.length() + 1;
148
        }
149 1 1. run : removed call to com/jsql/util/ThreadUtil::remove → NO_COVERAGE
        this.injectionModel.getMediatorUtils().threadUtil().remove(elementDatabase);
150 1 1. run : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::run → NO_COVERAGE
        return slidingWindowAllRows.toString();
151
    }
152
153
    private String decodeUrl(String currentChunk) {
154 1 1. decodeUrl : negated conditional → NO_COVERAGE
        if (!this.injectionModel.getMediatorUtils().preferencesUtil().isUrlDecodeDisabled()) {
155
            try {
156 1 1. decodeUrl : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE
                return URLDecoder.decode(currentChunk, StandardCharsets.UTF_8);  // Transform %00 entities to text
157
            } catch (IllegalArgumentException e) {
158
                LOGGER.log(LogLevelUtil.CONSOLE_JAVA, "Decoding fails on UT8, keeping raw result");
159
            }
160
        }
161 1 1. decodeUrl : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE
        return currentChunk;
162
    }
163
164
    private String decodeUnicode(String currentChunk, String initialSqlQuery) {
165
        if (
166 1 1. decodeUnicode : negated conditional → NO_COVERAGE
            !this.injectionModel.getMediatorUtils().preferencesUtil().isUnicodeDecodeDisabled()
167 2 1. decodeUnicode : negated conditional → NO_COVERAGE
2. decodeUnicode : negated conditional → NO_COVERAGE
            && !"select@@plugin_dir".equals(initialSqlQuery)  // can give C:\path\
168 1 1. decodeUnicode : negated conditional → NO_COVERAGE
            && initialSqlQuery != null && !initialSqlQuery.matches("(?si).*select.*sys_eval\\('.*'\\).*")
169
        ) {
170 1 1. decodeUnicode : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE
            return StringEscapeUtils.unescapeJava(  // transform \u0000 entities to text
171
                currentChunk
172
                .replaceAll("\\\\u.{0,3}$", StringUtils.EMPTY)  // remove incorrect entities
173
                .replaceAll("\\\\(\\d{4})", "\\\\u$1")  // transform PDO Error 10.11.3-MariaDB-1 \0000 entities
174
            );
175
        }
176 1 1. decodeUnicode : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE
        return currentChunk;
177
    }
178
179
    private String getQuery(String initialSqlQuery, int countAllRows) {
180 1 1. getQuery : replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::getQuery → NO_COVERAGE
        return initialSqlQuery.replace(LIMIT, this.injectionModel.getMediatorEngine().getEngine().instance().sqlLimit(countAllRows));
181
    }
182
183
    private void appendRowFixed(StringBuilder slidingWindowAllRows, StringBuilder slidingWindowCurrentRow) {
184
        // Check either if there is more than 1 row and if there is less than 1 complete row
185
        var regexAtLeastOneRow = Pattern.compile(
186
            String.format(
187
                "%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]%s%s%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$",
188
                MODE,
189
                ENCLOSE_VALUE_RGX,
190
                SEPARATOR_CELL_RGX,
191
                ENCLOSE_VALUE_RGX
192
            )
193
        )
194
        .matcher(slidingWindowCurrentRow);
195
        
196
        var regexRowIncomplete = Pattern.compile(
197
            MODE
198
            + ENCLOSE_VALUE_RGX
199
            + "[^\\x01-\\x03\\x05-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$"
200
        )
201
        .matcher(slidingWindowCurrentRow);
202
203
        // If there is more than 1 row, delete the last incomplete one in order to restart properly from it at the next loop,
204
        // else if there is 1 row but incomplete, mark it as cut with the letter c
205 1 1. appendRowFixed : negated conditional → NO_COVERAGE
        if (regexAtLeastOneRow.find()) {
206
            var allLine = slidingWindowAllRows.toString();
207 1 1. appendRowFixed : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
            slidingWindowAllRows.setLength(0);
208
            slidingWindowAllRows.append(
209
                Pattern.compile(
210
                    MODE
211
                    + ENCLOSE_VALUE_RGX
212
                    + "[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]+?$"
213
                )
214
                .matcher(allLine)
215
                .replaceAll(StringUtils.EMPTY)
216
            );
217
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Chunk unreliable, reloading row part...");
218 1 1. appendRowFixed : negated conditional → NO_COVERAGE
        } else if (regexRowIncomplete.find()) {
219
            slidingWindowAllRows.append(StringUtil.hexstr("05")).append("1").append(StringUtil.hexstr("0804"));
220
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, "Chunk unreliable, keeping row parts only");
221
        }
222
    }
223
224
    private void scrapeTrailJunk(StringBuilder slidingWindowCurrentRow) {
225
        // Remove everything after chunk
226
        // => \4xxxxxxxx\500\4\6\4...\4 => \1\3\3\7junk
227
        var currentRow = slidingWindowCurrentRow.toString();
228 1 1. scrapeTrailJunk : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
        slidingWindowCurrentRow.setLength(0);
229
        slidingWindowCurrentRow.append(
230
            Pattern.compile(MODE + TRAIL_RGX +".*")
231
            .matcher(currentRow)
232
            .replaceAll(StringUtils.EMPTY)
233
        );
234
    }
235
236
    private int getCountRows(StringBuilder slidingWindowCurrentRow) {
237
        var regexAtLeastOneRow = Pattern.compile(
238
            String.format(
239
                "%s(%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?%s[^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?\\x08?%s)",
240
                MODE,
241
                ENCLOSE_VALUE_RGX,
242
                SEPARATOR_QTE_RGX,
243
                ENCLOSE_VALUE_RGX
244
            )
245
        )
246
        .matcher(slidingWindowCurrentRow);
247
        var nbCompleteLine = 0;
248 1 1. getCountRows : negated conditional → NO_COVERAGE
        while (regexAtLeastOneRow.find()) {
249 1 1. getCountRows : Changed increment from 1 to -1 → NO_COVERAGE
            nbCompleteLine++;
250
        }
251 1 1. getCountRows : replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::getCountRows → NO_COVERAGE
        return nbCompleteLine;
252
    }
253
254
    private void endInjection(AbstractElementDatabase searchName, Throwable e) throws InjectionFailureException {
255
        // Premature end of results
256
        // if it's not the root (empty tree)
257 1 1. endInjection : negated conditional → NO_COVERAGE
        if (searchName != null) {
258 1 1. endInjection : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(new Seal.EndProgress(searchName));
259
        }
260
        var messageError = new StringBuilder("Fetching fails: no data to parse");
261 1 1. endInjection : negated conditional → NO_COVERAGE
        if (searchName != null) {
262
            messageError.append(" for ").append(StringUtil.detectUtf8(searchName.toString()));
263
        }
264 3 1. endInjection : negated conditional → NO_COVERAGE
2. endInjection : changed conditional boundary → NO_COVERAGE
3. endInjection : negated conditional → NO_COVERAGE
        if (searchName instanceof Table && searchName.getChildCount() > 0) {
265
            messageError.append(", check Network tab for logs");
266
        }
267
        throw new InjectionFailureException(messageError.toString(), e);
268
    }
269
270
    private void sendChunk(String currentChunk) {
271 1 1. sendChunk : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(new Seal.MessageChunk(
272
            Pattern.compile(MODE + TRAIL_RGX +".*")
273
            .matcher(currentChunk)
274
            .replaceAll(StringUtils.EMPTY)
275
            .replace("\\n", "\\\\\\n")
276
            .replace("\\r", "\\\\\\r")
277
            .replace("\\t", "\\\\\\t")
278
        ));
279
    }
280
281
    // TODO pb for same char string like aaaaaaaaaaaaa...aaaaaaaaaaaaa
282
    // detected as infinite
283
    private int checkInfinite(
284
        int loop,
285
        String previousChunk,
286
        String currentChunk,
287
        StringBuilder slidingWindowCurrentRow,
288
        StringBuilder slidingWindowAllRows
289
    ) throws LoopDetectedSlidingException {
290
        int infiniteLoop = loop;
291 1 1. checkInfinite : negated conditional → NO_COVERAGE
        if (previousChunk.equals(currentChunk)) {
292 1 1. checkInfinite : Changed increment from 1 to -1 → NO_COVERAGE
            infiniteLoop++;
293 2 1. checkInfinite : changed conditional boundary → NO_COVERAGE
2. checkInfinite : negated conditional → NO_COVERAGE
            if (infiniteLoop >= 20) {
294 1 1. checkInfinite : removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE
                this.stop();
295
                throw new LoopDetectedSlidingException(
296
                    slidingWindowAllRows.toString(),
297
                    slidingWindowCurrentRow.toString()
298
                );
299
            }
300
        }
301 1 1. checkInfinite : replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::checkInfinite → NO_COVERAGE
        return infiniteLoop;
302
    }
303
304
    private Matcher parseTrailOnlyFound(String sourcePage) {
305
        String sourcePageUnicodeDecoded = this.decodeUnicode(sourcePage, null);
306
        // TODO: prevent to find the last line directly: MODE + LEAD + .* + TRAIL_RGX
307
        // It creates extra query which can be endless if not nullified
308 1 1. parseTrailOnlyFound : replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseTrailOnlyFound → NO_COVERAGE
        return Pattern.compile(
309
            String.format("(?s)%s(?i)%s", LEAD, TRAIL_RGX)
310
        )
311
        .matcher(sourcePageUnicodeDecoded);
312
    }
313
314
    /**
315
     * After ${lead} tag, gets characters between 1 and maxPerf
316
     * performanceQuery() gets 65536 characters or fewer
317
     * [${lead}blahblah1337      ] : end or limit+1
318
     * [${lead}blahblah      blah] : continue substr()
319
     */
320
    private Matcher parseLeadFound(String sourcePage, String performanceLength) throws InjectionFailureException {
321
        Matcher regexAtLeastOneRow;
322
        try {
323
            regexAtLeastOneRow = Pattern.compile(
324
                String.format("(?s)%s(?i)(.{1,%s})", LEAD, performanceLength)
325
            )
326
            .matcher(sourcePage);
327
        } catch (PatternSyntaxException e) {
328
            // Fix #35382 : PatternSyntaxException null on SQLi(.{1,null})
329
            throw new InjectionFailureException("Row parsing failed using capacity", e);
330
        }
331 1 1. parseLeadFound : replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseLeadFound → NO_COVERAGE
        return regexAtLeastOneRow;
332
    }
333
334
    private void checkSuspend(
335
        AbstractStrategy strategy,
336
        StringBuilder slidingWindowAllRows,
337
        StringBuilder slidingWindowCurrentRow
338
    ) throws StoppedByUserSlidingException, InjectionFailureException {
339 1 1. checkSuspend : negated conditional → NO_COVERAGE
        if (this.isSuspended()) {
340
            throw new StoppedByUserSlidingException(
341
                slidingWindowAllRows.toString(),
342
                slidingWindowCurrentRow.toString()
343
            );
344 1 1. checkSuspend : negated conditional → NO_COVERAGE
        } else if (strategy == null) {
345
            // Fix #1905 : NullPointerException on injectionStrategy.inject()
346
            throw new InjectionFailureException("Undefined strategy");
347
        }
348
    }
349
350
    private void scrap(StringBuilder slidingWindowAllRows) {
351
        // Remove everything not properly attached to the last row:
352
        // 1. very start of a new row: XXXXX\4[\6\4]$
353
        // 2. very end of the last row: XXXXX[\500]$
354
        var allRowsLimit = slidingWindowAllRows.toString();
355 1 1. scrap : removed call to java/lang/StringBuilder::setLength → NO_COVERAGE
        slidingWindowAllRows.setLength(0);
356
        slidingWindowAllRows.append(
357
            Pattern.compile(
358
                String.format(
359
                    "%s(%s%s|%s\\d*)$",
360
                    MODE,
361
                    SEPARATOR_CELL_RGX,
362
                    ENCLOSE_VALUE_RGX,
363
                    SEPARATOR_QTE_RGX
364
                )
365
            )
366
            .matcher(allRowsLimit)
367
            .replaceAll(StringUtils.EMPTY)
368
        );
369
    }
370
371
    private void sendProgress(int numberToFind, int countProgress, AbstractElementDatabase searchName) {
372 3 1. sendProgress : negated conditional → NO_COVERAGE
2. sendProgress : negated conditional → NO_COVERAGE
3. sendProgress : changed conditional boundary → NO_COVERAGE
        if (numberToFind > 0 && searchName != null) {
373 1 1. sendProgress : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
            this.injectionModel.sendToViews(new Seal.UpdateProgress(searchName, countProgress));
374
        }
375
    }
376
    
377
    public static List<List<String>> parse(String rows) throws InjectionFailureException {
378
        // Parse all the data we have retrieved
379
        var regexSearch = Pattern.compile(
380
                String.format(
381
                    "%s%s([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?)%s([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*?)(\\x08)?%s",
382
                    MODE,
383
                    ENCLOSE_VALUE_RGX,
384
                    SEPARATOR_QTE_RGX,
385
                    ENCLOSE_VALUE_RGX
386
                )
387
            )
388
            .matcher(rows);
389 1 1. parse : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
390
            throw new InjectionFailureException();
391
        }
392
        regexSearch.reset();
393
        var rowsFound = 0;
394
        List<List<String>> listValues = new ArrayList<>();
395
396
        // Build a 2D array of strings from the data we have parsed
397
        // => row number, occurrence, value1, value2...
398 1 1. parse : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
399
            String values = regexSearch.group(1);
400
            var instances = Integer.parseInt(regexSearch.group(2));
401
402
            listValues.add(new ArrayList<>());
403 1 1. parse : Replaced integer addition with subtraction → NO_COVERAGE
            listValues.get(rowsFound).add(Integer.toString(rowsFound + 1));
404
            listValues.get(rowsFound).add("x"+ instances);
405
            for (String cellValue: values.split("\\x7F", -1)) {
406
                listValues.get(rowsFound).add(cellValue);
407
            }
408 1 1. parse : Changed increment from 1 to -1 → NO_COVERAGE
            rowsFound++;
409
        }
410 1 1. parse : replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetRows::parse → NO_COVERAGE
        return listValues;
411
    }
412
}

Mutations

59

1.1
Location : run
Killed by : none
removed call to com/jsql/util/ThreadUtil::put → NO_COVERAGE

64

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

80

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::checkSuspend → NO_COVERAGE

88

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : run
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : run
Killed by : none
negated conditional → NO_COVERAGE

90

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

92

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

108

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendChunk → NO_COVERAGE

110

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::endInjection → NO_COVERAGE

115

1.1
Location : run
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

2.2
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

119

1.1
Location : run
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : run
Killed by : none
negated conditional → NO_COVERAGE

121

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

123

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrapeTrailJunk → NO_COVERAGE

126

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

127

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE

128

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::scrap → NO_COVERAGE

129

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::appendRowFixed → NO_COVERAGE

132

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

135

1.1
Location : run
Killed by : none
negated conditional → NO_COVERAGE

141

1.1
Location : run
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

143

1.1
Location : run
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::sendProgress → NO_COVERAGE

147

1.1
Location : run
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

149

1.1
Location : run
Killed by : none
removed call to com/jsql/util/ThreadUtil::remove → NO_COVERAGE

150

1.1
Location : run
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::run → NO_COVERAGE

154

1.1
Location : decodeUrl
Killed by : none
negated conditional → NO_COVERAGE

156

1.1
Location : decodeUrl
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE

161

1.1
Location : decodeUrl
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUrl → NO_COVERAGE

166

1.1
Location : decodeUnicode
Killed by : none
negated conditional → NO_COVERAGE

167

1.1
Location : decodeUnicode
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : decodeUnicode
Killed by : none
negated conditional → NO_COVERAGE

168

1.1
Location : decodeUnicode
Killed by : none
negated conditional → NO_COVERAGE

170

1.1
Location : decodeUnicode
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE

176

1.1
Location : decodeUnicode
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::decodeUnicode → NO_COVERAGE

180

1.1
Location : getQuery
Killed by : none
replaced return value with "" for com/jsql/model/suspendable/SuspendableGetRows::getQuery → NO_COVERAGE

205

1.1
Location : appendRowFixed
Killed by : none
negated conditional → NO_COVERAGE

207

1.1
Location : appendRowFixed
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

218

1.1
Location : appendRowFixed
Killed by : none
negated conditional → NO_COVERAGE

228

1.1
Location : scrapeTrailJunk
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

248

1.1
Location : getCountRows
Killed by : none
negated conditional → NO_COVERAGE

249

1.1
Location : getCountRows
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

251

1.1
Location : getCountRows
Killed by : none
replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::getCountRows → NO_COVERAGE

257

1.1
Location : endInjection
Killed by : none
negated conditional → NO_COVERAGE

258

1.1
Location : endInjection
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

261

1.1
Location : endInjection
Killed by : none
negated conditional → NO_COVERAGE

264

1.1
Location : endInjection
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : endInjection
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : endInjection
Killed by : none
negated conditional → NO_COVERAGE

271

1.1
Location : sendChunk
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

291

1.1
Location : checkInfinite
Killed by : none
negated conditional → NO_COVERAGE

292

1.1
Location : checkInfinite
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

293

1.1
Location : checkInfinite
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : checkInfinite
Killed by : none
negated conditional → NO_COVERAGE

294

1.1
Location : checkInfinite
Killed by : none
removed call to com/jsql/model/suspendable/SuspendableGetRows::stop → NO_COVERAGE

301

1.1
Location : checkInfinite
Killed by : none
replaced int return with 0 for com/jsql/model/suspendable/SuspendableGetRows::checkInfinite → NO_COVERAGE

308

1.1
Location : parseTrailOnlyFound
Killed by : none
replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseTrailOnlyFound → NO_COVERAGE

331

1.1
Location : parseLeadFound
Killed by : none
replaced return value with null for com/jsql/model/suspendable/SuspendableGetRows::parseLeadFound → NO_COVERAGE

339

1.1
Location : checkSuspend
Killed by : none
negated conditional → NO_COVERAGE

344

1.1
Location : checkSuspend
Killed by : none
negated conditional → NO_COVERAGE

355

1.1
Location : scrap
Killed by : none
removed call to java/lang/StringBuilder::setLength → NO_COVERAGE

372

1.1
Location : sendProgress
Killed by : none
negated conditional → NO_COVERAGE

2.2
Location : sendProgress
Killed by : none
negated conditional → NO_COVERAGE

3.3
Location : sendProgress
Killed by : none
changed conditional boundary → NO_COVERAGE

373

1.1
Location : sendProgress
Killed by : none
removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE

389

1.1
Location : parse
Killed by : none
negated conditional → NO_COVERAGE

398

1.1
Location : parse
Killed by : none
negated conditional → NO_COVERAGE

403

1.1
Location : parse
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

408

1.1
Location : parse
Killed by : none
Changed increment from 1 to -1 → NO_COVERAGE

410

1.1
Location : parse
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/suspendable/SuspendableGetRows::parse → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.1