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