DataAccess.java

1
/*******************************************************************************
2
 * Copyhacked (H) 2012-2025.
3
 * This program and the accompanying materials
4
 * are made available under no term at all, use it like
5
 * you want, but share and discuss it
6
 * every time possible with every body.
7
 * 
8
 * Contributors:
9
 *      ron190 at ymail dot com - initial implementation
10
 ******************************************************************************/
11
package com.jsql.model.accessible;
12
13
import com.jsql.model.InjectionModel;
14
import com.jsql.model.bean.database.*;
15
import com.jsql.model.bean.util.Interaction;
16
import com.jsql.model.bean.util.Request;
17
import com.jsql.model.exception.AbstractSlidingException;
18
import com.jsql.model.exception.InjectionFailureException;
19
import com.jsql.model.exception.JSqlException;
20
import com.jsql.model.suspendable.SuspendableGetRows;
21
import com.jsql.util.I18nUtil;
22
import com.jsql.util.LogLevelUtil;
23
import org.apache.commons.lang3.StringUtils;
24
import org.apache.logging.log4j.LogManager;
25
import org.apache.logging.log4j.Logger;
26
27
import java.util.ArrayList;
28
import java.util.List;
29
import java.util.regex.Pattern;
30
31
/**
32
 * Database resource object to read name of databases, tables, columns and values
33
 * using most suited injection strategy.
34
 */
35
public class DataAccess {
36
    
37
    /**
38
     * Log4j logger sent to view.
39
     */
40
    private static final Logger LOGGER = LogManager.getRootLogger();
41
    
42
    /**
43
     * Regex characters marking the end of the result of an injection.
44
     * Process stops when this schema is encountered:
45
     * <pre>SQLix01x03x03x07
46
     */
47
    public static final String TRAIL_RGX = "\\x01\\x03\\x03\\x07";
48
    
49
    /**
50
     * Regex character used between each table cells.
51
     * Expected schema of multiple table cells :
52
     * <pre>
53
     * x04[table cell]x05[number of occurrences]x04x06x04[table cell]x05[number of occurrences]x04
54
     */
55
    public static final String SEPARATOR_CELL_RGX = "\\x06";
56
    
57
    /**
58
     * Regex character used between the table cell and the number of occurrence of the cell text.
59
     * Expected schema of a table cell data is
60
     * <pre>x04[table cell]x05[number of occurrences]x04
61
     */
62
    public static final String SEPARATOR_QTE_RGX = "\\x05";
63
64
    /**
65
     * Regex character enclosing a table cell returned by injection.
66
     * It allows to detect the correct end of a table cell data during parsing.
67
     * Expected schema of a table cell data is
68
     * <pre>x04[table cell]x05[number of occurrences]x04
69
     */
70
    public static final String ENCLOSE_VALUE_RGX = "\\x04";
71
    
72
    public static final String LEAD = "SqLi";
73
    public static final String SHELL_LEAD = "${shell.lead}";
74
    public static final String TRAIL = "iLQS";
75
    public static final String SHELL_TRAIL = "${shell.trail}";
76
    
77
    /**
78
     * Regex keywords corresponding to multiline and case-insensitive match.
79
     */
80
    public static final String MODE = "(?si)";
81
    
82
    /**
83
     * Regex schema describing a table cell with firstly the cell content and secondly the number of occurrences
84
     * of the cell text, separated by the reserved character x05 in hexadecimal.
85
     * The range of characters from x01 to x1F are not printable ASCII characters used to parse the data and exclude
86
     * printable characters during parsing.
87
     * Expected schema of a table cell data is
88
     * <pre>x04[table cell]x05[number of occurrences]x04
89
     */
90
    public static final String CELL_TABLE = "([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)"+ DataAccess.SEPARATOR_QTE_RGX +"([^\\x01-\\x09\\x0B-\\x0C\\x0E-\\x1F]*)(\\x08)?";
91
92
    private final InjectionModel injectionModel;
93
    
94
    public DataAccess(InjectionModel injectionModel) {
95
        this.injectionModel = injectionModel;
96
    }
97
    
98
    /**
99
     * Get general database information.<br>
100
     * => version{%}database{%}user{%}CURRENT_USER
101
     */
102
    public void getDatabaseInfos() {
103
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_INFORMATIONS"));
104
        
105
        var sourcePage = new String[]{ StringUtils.EMPTY };
106
107
        var resultToParse = StringUtils.EMPTY;
108
        try {
109
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
110
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlInfos(),
111
                sourcePage,
112
                false,
113
                0,
114
                MockElement.MOCK,
115
                "metadata"
116
            );
117
        } catch (AbstractSlidingException e) {
118
            resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
119
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
120
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
121
        }
122
123 1 1. getDatabaseInfos : negated conditional → NO_COVERAGE
        if (StringUtils.isEmpty(resultToParse)) {
124 1 1. getDatabaseInfos : removed call to com/jsql/model/InjectionModel::sendResponseFromSite → NO_COVERAGE
            this.injectionModel.sendResponseFromSite("Incorrect metadata", sourcePage[0].trim());
125
        }
126
        
127
        try {
128
            String versionDatabase = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[0].replaceAll("\\s+", StringUtils.SPACE);
129
            String nameDatabase = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[1];
130
            String username = resultToParse.split(DataAccess.ENCLOSE_VALUE_RGX)[2];
131
132
            var infos = String.format(
133
                "Database [%s] on %s [%s] for user [%s]",
134
                nameDatabase,
135
                this.injectionModel.getMediatorVendor().getVendor(),
136
                versionDatabase,
137
                username
138
            );
139
            LOGGER.log(LogLevelUtil.CONSOLE_SUCCESS, infos);
140
        } catch (ArrayIndexOutOfBoundsException e) {
141
            LOGGER.log(
142
                LogLevelUtil.CONSOLE_ERROR,
143
                String.format("%s: %s", I18nUtil.valueByKey("LOG_DB_METADATA_INCORRECT"), resultToParse)
144
            );
145
            LOGGER.log(LogLevelUtil.CONSOLE_INFORM, I18nUtil.valueByKey("LOG_DB_METADATA_WARN"));
146
        }
147
    }
148
149
    /**
150
     * Get database names and table counts and send them to the view.<br>
151
     * Use readable text (not hexa) and parse this pattern:<br>
152
     * => hh[database name 1]jj[table count]hhgghh[database name 2]jj[table count]hhggh...hi<br>
153
     * Data window can be cut before the end of the request but the process helps to obtain
154
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
155
     * @return list of databases found
156
     * @throws JSqlException when injection failure or stopped by user
157
     */
158
    public List<Database> listDatabases() throws JSqlException {
159
        LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, () -> I18nUtil.valueByKey("LOG_FETCHING_DATABASES"));
160
        List<Database> databases = new ArrayList<>();
161
        String resultToParse = StringUtils.EMPTY;
162
        
163
        try {
164
            var sourcePage = new String[]{ StringUtils.EMPTY };
165
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
166
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlDatabases(),
167
                sourcePage,
168
                true,
169
                0,
170
                MockElement.MOCK,
171
                "databases"
172
            );
173
        } catch (AbstractSlidingException e) {
174
            resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
175
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
176
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
177
        }
178
179
        // Parse all data we have retrieved
180
        var regexSearch = Pattern.compile(
181
                DataAccess.MODE
182
                + DataAccess.ENCLOSE_VALUE_RGX
183
                + DataAccess.CELL_TABLE
184
                + DataAccess.ENCLOSE_VALUE_RGX
185
            )
186
            .matcher(resultToParse);
187 1 1. listDatabases : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
188
            throw new InjectionFailureException("No match while injecting databases");
189
        }
190
        
191
        regexSearch.reset();
192
        // Build an array of Database objects from the data we have parsed
193 1 1. listDatabases : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
194
            String databaseName = regexSearch.group(1);
195
            String tableCount = regexSearch.group(2);
196
197
            var newDatabase = new Database(databaseName, tableCount);
198
            databases.add(newDatabase);
199
        }
200
201
        var request = new Request();
202 1 1. listDatabases : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.ADD_DATABASES);
203 1 1. listDatabases : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(databases);
204 1 1. listDatabases : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
205 1 1. listDatabases : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listDatabases → NO_COVERAGE
        return databases;
206
    }
207
208
    /**
209
     * Get tables name and row count and send them to the view.<br>
210
     * Use readable text (not hexa) and parse this pattern:<br>
211
     * => hh[table name 1]jj[rows count]hhgghh[table name 2]jj[rows count]hhggh...hi<br>
212
     * Data window can be cut before the end of the request but the process helps to obtain
213
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
214
     * @param database which contains tables to find
215
     * @return list of tables found
216
     * @throws JSqlException when injection failure or stopped by user
217
     */
218
    public List<Table> listTables(Database database) throws JSqlException {
219
        // Reset stoppedByUser if list of Databases is partial
220
        // and some Tables are still reachable
221 1 1. listTables : removed call to com/jsql/model/InjectionModel::setIsStoppedByUser → NO_COVERAGE
        this.injectionModel.setIsStoppedByUser(false);
222
        
223
        // Inform the view that database has just been used
224
        var requestStartProgress = new Request();
225 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestStartProgress.setMessage(Interaction.START_PROGRESS);
226 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestStartProgress.setParameters(database);
227 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestStartProgress);
228
229
        var tableCount = Integer.toString(database.getChildCount());
230
        
231
        String resultToParse = StringUtils.EMPTY;
232
        try {
233
            var pageSource = new String[]{ StringUtils.EMPTY };
234
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
235
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlTables(database),
236
                pageSource,
237
                true,
238
                Integer.parseInt(tableCount),
239
                database,
240
                "tables"
241
            );
242
        } catch (AbstractSlidingException e) {
243
            resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
244
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
245
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
246
        }
247
248
        // Parse all the data we have retrieved
249
        var regexSearch = Pattern.compile(
250
                DataAccess.MODE
251
                + DataAccess.ENCLOSE_VALUE_RGX
252
                + DataAccess.CELL_TABLE
253
                + DataAccess.ENCLOSE_VALUE_RGX
254
            )
255
            .matcher(resultToParse);
256
        
257
        var requestEndProgress = new Request();
258 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_PROGRESS);
259 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(database);
260 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
261
        
262 1 1. listTables : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
263
            throw new InjectionFailureException("No match while injecting tables");
264
        }
265
        regexSearch.reset();
266
        
267
        List<Table> tables = new ArrayList<>();
268
        
269
        // Build an array of Table objects from the data we have parsed
270 1 1. listTables : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
271
            String tableName = regexSearch.group(1);
272
            String rowCount = regexSearch.group(2);
273
            
274
            var newTable = new Table(tableName, rowCount, database);
275
            tables.add(newTable);
276
        }
277
        
278
        var requestAddTables = new Request();
279 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestAddTables.setMessage(Interaction.ADD_TABLES);
280 1 1. listTables : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestAddTables.setParameters(tables);
281 1 1. listTables : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestAddTables);
282 1 1. listTables : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listTables → NO_COVERAGE
        return tables;
283
    }
284
285
    /**
286
     * Get column names and send them to the view.<br>
287
     * Use readable text (not hexa) and parse this pattern with 2nd member forced to 31 (1 in ascii):<br>
288
     * => hh[column name 1]jj[31]hhgghh[column name 2]jj[31]hhggh...hi<br>
289
     * Data window can be cut before the end of the request but the process helps to obtain
290
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
291
     * @param table which contains columns to find
292
     * @return list of columns found
293
     * @throws JSqlException when injection failure or stopped by user
294
     */
295
    public List<Column> listColumns(Table table) throws JSqlException {
296
        List<Column> columns = new ArrayList<>();
297
        
298
        // Inform the view that table has just been used
299
        var requestStartProgress = new Request();
300 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestStartProgress.setMessage(Interaction.START_INDETERMINATE_PROGRESS);
301 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestStartProgress.setParameters(table);
302 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestStartProgress);
303
304
        String resultToParse = StringUtils.EMPTY;
305
        
306
        try {
307
            var pageSource = new String[]{ StringUtils.EMPTY };
308
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
309
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlColumns(table),
310
                pageSource,
311
                true,
312
                0,
313
                table,
314
                "columns"
315
            );
316
        } catch (AbstractSlidingException e) {
317
            resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
318
        } catch (Exception e) {  // Catch all exceptions but prevent detecting bug
319
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
320
        }
321
322
        // Build SQLite columns
323 1 1. listColumns : negated conditional → NO_COVERAGE
        if (this.injectionModel.getMediatorVendor().isSqlite()) {
324
            resultToParse = this.injectionModel.getMediatorVendor().getSqlite().transformSqlite(resultToParse);
325
        }
326
        
327
        // Parse all the data we have retrieved
328
        var regexSearch = Pattern.compile(
329
                DataAccess.MODE
330
                + DataAccess.ENCLOSE_VALUE_RGX
331
                + DataAccess.CELL_TABLE
332
                + DataAccess.ENCLOSE_VALUE_RGX
333
            )
334
            .matcher(resultToParse);
335
336
        var requestEndProgress = new Request();
337 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_INDETERMINATE_PROGRESS);
338 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(table);
339 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
340
341 1 1. listColumns : negated conditional → NO_COVERAGE
        if (!regexSearch.find()) {
342
            throw new InjectionFailureException("No match while injecting columns");
343
        }
344
345
        regexSearch.reset();
346
347
        // Build an array of Column objects from the data we have parsed
348 1 1. listColumns : negated conditional → NO_COVERAGE
        while (regexSearch.find()) {
349
            String nameColumn = regexSearch.group(1);
350
            var column = new Column(nameColumn, table);
351
            columns.add(column);
352
        }
353
354
        var requestAddColumns = new Request();
355 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestAddColumns.setMessage(Interaction.ADD_COLUMNS);
356 1 1. listColumns : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestAddColumns.setParameters(columns);
357 1 1. listColumns : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestAddColumns);
358 1 1. listColumns : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listColumns → NO_COVERAGE
        return columns;
359
    }
360
361
    /**
362
     * Get table values and count each occurrence and send it to the view.<br>
363
     * Values are on clear text (not hexa) and follows this window pattern<br>
364
     * => hh[value 1]jj[count]hhgghh[value 2]jj[count]hhggh...hi<br>
365
     * Data window can be cut before the end of the request but the process helps to obtain
366
     * the rest of the unreachable data. The process can be interrupted by the user (stop/pause).
367
     * @param columnsBean choice by the user
368
     * @return a 2x2 table containing values by columns
369
     * @throws JSqlException when injection failure or stopped by user
370
     */
371
    public String[][] listValues(List<Column> columnsBean) throws JSqlException {
372
        var databaseBean = (Database) columnsBean.get(0).getParent().getParent();
373
        var tableBean = (Table) columnsBean.get(0).getParent();
374
        int rowCount = columnsBean.get(0).getParent().getChildCount();
375
376
        // Inform the view that table has just been used
377
        var request = new Request();
378 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        request.setMessage(Interaction.START_PROGRESS);
379 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        request.setParameters(tableBean);
380 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(request);
381
382
        // Build an array of column names
383
        List<String> columnsName = new ArrayList<>();
384
        for (AbstractElementDatabase columnBean: columnsBean) {
385
            columnsName.add(columnBean.toString());
386
        }
387
388
        // From that array, build the SQL fields nicely
389
        // => col1{%}col2...
390
        // ==> trim(ifnull(`col1`,0x00)),0x7f,trim(ifnull(`Col2`,0x00))...
391
        String[] columns = columnsName.toArray(new String[0]);
392
393
        List<List<String>> listValues = this.getRows(databaseBean, tableBean, rowCount, columns);
394
395
        // Add the default title to the columns: row number, occurrence
396 1 1. listValues : removed call to java/util/List::add → NO_COVERAGE
        columnsName.add(0, StringUtils.EMPTY);
397 1 1. listValues : removed call to java/util/List::add → NO_COVERAGE
        columnsName.add(0, StringUtils.EMPTY);
398
399
        String[][] table = this.getTable(columnsName, listValues);
400
401
        columns = columnsName.toArray(new String[0]);
402
        
403
        // Group the columns names, values and Table object in one array
404
        var objectData = new Object[]{ columns, table, tableBean };
405
406
        var requestCreateValuesTab = new Request();
407 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestCreateValuesTab.setMessage(Interaction.CREATE_VALUES_TAB);
408 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestCreateValuesTab.setParameters(objectData);
409 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestCreateValuesTab);
410
411
        var requestEndProgress = new Request();
412 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE
        requestEndProgress.setMessage(Interaction.END_PROGRESS);
413 1 1. listValues : removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE
        requestEndProgress.setParameters(tableBean);
414 1 1. listValues : removed call to com/jsql/model/InjectionModel::sendToViews → NO_COVERAGE
        this.injectionModel.sendToViews(requestEndProgress);
415 1 1. listValues : replaced return value with null for com/jsql/model/accessible/DataAccess::listValues → NO_COVERAGE
        return table;
416
    }
417
418
    private List<List<String>> getRows(Database database, Table table, int rowCount, String[] columns) throws InjectionFailureException {
419
        String resultToParse = StringUtils.EMPTY;
420
        
421
        try {
422
            var pageSource = new String[]{ StringUtils.EMPTY };
423
            resultToParse = new SuspendableGetRows(this.injectionModel).run(
424
                this.injectionModel.getMediatorVendor().getVendor().instance().sqlRows(columns, database, table),
425
                pageSource,
426
                true,
427
                rowCount,
428
                table,
429
                "rows"
430
            );
431
        } catch (AbstractSlidingException e) {  // Catch all exceptions but prevent detecting bug
432
            resultToParse = DataAccess.getPartialResultAndLog(e, resultToParse);
433
        } catch (Exception e) {
434
            LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage(), e);
435
        }
436 1 1. getRows : replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::getRows → NO_COVERAGE
        return SuspendableGetRows.parse(resultToParse);
437
    }
438
439
    private static String getPartialResultAndLog(AbstractSlidingException e, String resultToParse) {
440
        LOGGER.log(LogLevelUtil.CONSOLE_ERROR, e.getMessage());
441
442
        // Get pieces of data already retrieved instead of losing them
443 1 1. getPartialResultAndLog : negated conditional → NO_COVERAGE
        if (StringUtils.isNotEmpty(e.getSlidingWindowAllRows())) {
444
            resultToParse = e.getSlidingWindowAllRows();
445 1 1. getPartialResultAndLog : negated conditional → NO_COVERAGE
        } else if (StringUtils.isNotEmpty(e.getSlidingWindowCurrentRows())) {
446
            resultToParse = e.getSlidingWindowCurrentRows();
447
        }
448 1 1. getPartialResultAndLog : replaced return value with "" for com/jsql/model/accessible/DataAccess::getPartialResultAndLog → NO_COVERAGE
        return resultToParse;
449
    }
450
451
    private String[][] getTable(List<String> columnsName, List<List<String>> values) {
452
        // Build a proper 2D array from the data
453
        var table = new String[values.size()][columnsName.size()];
454
        
455 2 1. getTable : changed conditional boundary → NO_COVERAGE
2. getTable : negated conditional → NO_COVERAGE
        for (var indexRow = 0 ; indexRow < values.size() ; indexRow++) {
456
            var isIncomplete = false;
457
            
458 2 1. getTable : negated conditional → NO_COVERAGE
2. getTable : changed conditional boundary → NO_COVERAGE
            for (var indexColumn = 0 ; indexColumn < columnsName.size() ; indexColumn++) {
459
                try {
460
                    table[indexRow][indexColumn] = values.get(indexRow).get(indexColumn);
461
                } catch (IndexOutOfBoundsException e) {
462
                    isIncomplete = true;
463
                    LOGGER.log(LogLevelUtil.CONSOLE_DEFAULT, I18nUtil.valueByKey("LOG_LIST_VALUES_INCOMPLETE"));
464
                    LOGGER.log(LogLevelUtil.IGNORE, e);
465
                }
466
            }
467
            
468 1 1. getTable : negated conditional → NO_COVERAGE
            if (isIncomplete) {
469
                int logIndexRow = indexRow;
470
                LOGGER.log(
471
                    LogLevelUtil.CONSOLE_ERROR,
472
                    "{}{}: ",
473 1 1. lambda$getTable$2 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$2 → NO_COVERAGE
                    () -> I18nUtil.valueByKey("LOG_LIST_VALUES_TOO_LONG"),
474 2 1. lambda$getTable$3 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$3 → NO_COVERAGE
2. lambda$getTable$3 : Replaced integer addition with subtraction → NO_COVERAGE
                    () -> logIndexRow + 1
475
                );
476
477
                LOGGER.log(
478
                    LogLevelUtil.CONSOLE_ERROR,
479 1 1. lambda$getTable$4 : replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$4 → NO_COVERAGE
                    () -> String.join(
480
                        ", ",
481
                        values.get(logIndexRow).toArray(new String[0])
482
                    )
483
                );
484
            }
485
        }
486 1 1. getTable : replaced return value with null for com/jsql/model/accessible/DataAccess::getTable → NO_COVERAGE
        return table;
487
    }
488
}

Mutations

123

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

124

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

187

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

193

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

202

1.1
Location : listDatabases
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

203

1.1
Location : listDatabases
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

204

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

205

1.1
Location : listDatabases
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listDatabases → NO_COVERAGE

221

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

225

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

226

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

227

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

258

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

259

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

260

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

262

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

270

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

279

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

280

1.1
Location : listTables
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

281

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

282

1.1
Location : listTables
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listTables → NO_COVERAGE

300

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

301

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

302

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

323

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

337

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

338

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

339

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

341

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

348

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

355

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

356

1.1
Location : listColumns
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

357

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

358

1.1
Location : listColumns
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::listColumns → NO_COVERAGE

378

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

379

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

380

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

396

1.1
Location : listValues
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

397

1.1
Location : listValues
Killed by : none
removed call to java/util/List::add → NO_COVERAGE

407

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

408

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

409

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

412

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setMessage → NO_COVERAGE

413

1.1
Location : listValues
Killed by : none
removed call to com/jsql/model/bean/util/Request::setParameters → NO_COVERAGE

414

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

415

1.1
Location : listValues
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::listValues → NO_COVERAGE

436

1.1
Location : getRows
Killed by : none
replaced return value with Collections.emptyList for com/jsql/model/accessible/DataAccess::getRows → NO_COVERAGE

443

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

445

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

448

1.1
Location : getPartialResultAndLog
Killed by : none
replaced return value with "" for com/jsql/model/accessible/DataAccess::getPartialResultAndLog → NO_COVERAGE

455

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

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

458

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

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

468

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

473

1.1
Location : lambda$getTable$2
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$2 → NO_COVERAGE

474

1.1
Location : lambda$getTable$3
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$3 → NO_COVERAGE

2.2
Location : lambda$getTable$3
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

479

1.1
Location : lambda$getTable$4
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::lambda$getTable$4 → NO_COVERAGE

486

1.1
Location : getTable
Killed by : none
replaced return value with null for com/jsql/model/accessible/DataAccess::getTable → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.19.1