mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-06-08 21:38:21 +00:00
WIP SQL console: basic prototype working
This commit is contained in:
parent
2352bdcdd7
commit
0691d17867
@ -19,7 +19,6 @@
|
||||
|
||||
package tech.ailef.dbadmin.external.controller;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -57,6 +56,9 @@ import tech.ailef.dbadmin.external.DbAdminProperties;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbAdminRepository;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbObject;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
||||
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryOutputField;
|
||||
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryResult;
|
||||
import tech.ailef.dbadmin.external.dbmapping.query.DbQueryResultRow;
|
||||
import tech.ailef.dbadmin.external.dto.CompareOperator;
|
||||
import tech.ailef.dbadmin.external.dto.FacetedSearchRequest;
|
||||
import tech.ailef.dbadmin.external.dto.LogsSearchRequest;
|
||||
@ -543,10 +545,10 @@ public class DefaultDbAdminController {
|
||||
@GetMapping("/console")
|
||||
public String console(Model model, @RequestParam(required=false) String query) {
|
||||
model.addAttribute("activePage", "console");
|
||||
|
||||
model.addAttribute("query", query == null ? "" : query);
|
||||
if (query != null) {
|
||||
List<Map<String, Object>> results = jdbTemplate.query(query, (rs, rowNum) -> {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
List<DbQueryResultRow> results = jdbTemplate.query(query, (rs, rowNum) -> {
|
||||
Map<DbQueryOutputField, Object> result = new HashMap<>();
|
||||
|
||||
ResultSetMetaData metaData = rs.getMetaData();
|
||||
int cols = metaData.getColumnCount();
|
||||
@ -554,23 +556,15 @@ public class DefaultDbAdminController {
|
||||
for (int i = 0; i < cols; i++) {
|
||||
Object o = rs.getObject(i + 1);
|
||||
String columnName = metaData.getColumnName(i + 1);
|
||||
result.put(columnName, o);
|
||||
String tableName = metaData.getTableName(i + 1);
|
||||
DbQueryOutputField field = new DbQueryOutputField(columnName, tableName, dbAdmin);
|
||||
|
||||
result.put(field, o);
|
||||
}
|
||||
|
||||
return result;
|
||||
return new DbQueryResultRow(result, query);
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Print each map in a tabular format
|
||||
*/
|
||||
for (Map<String, Object> obj : results) {
|
||||
System.out.println("-----------------------------------------------------------");
|
||||
for (String key : obj.keySet()) {
|
||||
System.out.printf("%-20s | %s\n", key, obj.get(key));
|
||||
}
|
||||
|
||||
}
|
||||
model.addAttribute("results", new DbQueryResult(results));
|
||||
}
|
||||
|
||||
|
||||
|
74
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryOutputField.java
vendored
Normal file
74
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryOutputField.java
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import tech.ailef.dbadmin.external.DbAdmin;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbField;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
||||
import tech.ailef.dbadmin.external.exceptions.DbAdminException;
|
||||
|
||||
public class DbQueryOutputField {
|
||||
private String name;
|
||||
|
||||
private String table;
|
||||
|
||||
private DbField dbField;
|
||||
|
||||
public DbQueryOutputField(String name, String table, DbAdmin dbAdmin) {
|
||||
this.name = name;
|
||||
this.table = table;
|
||||
|
||||
try {
|
||||
DbObjectSchema schema = dbAdmin.findSchemaByTableName(table);
|
||||
DbField dbField = schema.getFieldByName(name);
|
||||
this.dbField = dbField;
|
||||
} catch (DbAdminException e) {
|
||||
// We were unable to map this result column to a table, this happens
|
||||
// for example with COUNT(*) results and similar. We ignore this
|
||||
// as the dbField will be null and handled as such in the rest of the code
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getTable() {
|
||||
return table;
|
||||
}
|
||||
|
||||
public boolean isPrimaryKey() {
|
||||
return dbField != null && dbField.isPrimaryKey();
|
||||
}
|
||||
|
||||
public boolean isForeignKey() {
|
||||
return dbField != null && dbField.isForeignKey();
|
||||
}
|
||||
|
||||
public boolean isBinary() {
|
||||
return dbField != null && dbField.isBinary();
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return "TODO TYPE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, table);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DbQueryOutputField other = (DbQueryOutputField) obj;
|
||||
return Objects.equals(name, other.name) && Objects.equals(table, other.table);
|
||||
}
|
||||
|
||||
|
||||
}
|
32
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResult.java
vendored
Normal file
32
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResult.java
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DbQueryResult {
|
||||
private List<DbQueryResultRow> rows;
|
||||
|
||||
public DbQueryResult(List<DbQueryResultRow> rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
|
||||
public List<DbQueryResultRow> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
public List<DbQueryOutputField> getSortedFields() {
|
||||
if (isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
} else {
|
||||
return rows.get(0).getSortedFields();
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return rows.size();
|
||||
}
|
||||
}
|
31
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResultRow.java
vendored
Normal file
31
src/main/java/tech/ailef/dbadmin/external/dbmapping/query/DbQueryResultRow.java
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package tech.ailef.dbadmin.external.dbmapping.query;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class DbQueryResultRow {
|
||||
private Map<DbQueryOutputField, Object> values;
|
||||
|
||||
private String query;
|
||||
|
||||
public DbQueryResultRow(Map<DbQueryOutputField, Object> values, String query) {
|
||||
this.values = values;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public List<DbQueryOutputField> getSortedFields() {
|
||||
return values.keySet().stream().sorted((f1, f2) -> f1.getName().compareTo(f2.getName())).toList();
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public Object get(DbQueryOutputField field) {
|
||||
return values.get(field);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -6,3 +6,4 @@
|
||||
#spring.h2.console.enabled=true
|
||||
|
||||
#spring.jpa.show-sql=true
|
||||
|
||||
|
@ -13,9 +13,14 @@
|
||||
<div class="col">
|
||||
<div class="box">
|
||||
<form th:action="|/${dbadmin_baseUrl}/console|" method="GET">
|
||||
<textarea class="form-control" rows="6" name="query"></textarea>
|
||||
<input class="ui-btn btn btn-primary" type="submit" value="Run">
|
||||
<textarea class="form-control" rows="6" name="query"
|
||||
th:text="${query}"></textarea>
|
||||
<input class="ui-btn btn btn-primary mt-3" type="submit" value="Run">
|
||||
</form>
|
||||
<div class="separator mt-3 mb-3"></div>
|
||||
<div th:replace="~{fragments/generic_table :: table(results=${results})}"></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
23
src/main/resources/templates/fragments/generic_data_row.html
Normal file
23
src/main/resources/templates/fragments/generic_data_row.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
|
||||
<head></head>
|
||||
<body>
|
||||
<tr th:fragment="data_row(row)" class="table-data-row">
|
||||
<td th:each="field : ${row.getSortedFields()}"
|
||||
th:classAppend="${field.isBinary() ? 'text-center' : ''}">
|
||||
<th:block th:replace="~{fragments/generic_data_row :: data_row_field(field=${field}, object=${row})}"></th:block>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<!-- data-row-field fragment -->
|
||||
<th:block th:fragment="data_row_field(field, object)">
|
||||
<span th:text="${object.get(field)}"></span>
|
||||
</th:block>
|
||||
<!-- end data-row-field fragment -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
41
src/main/resources/templates/fragments/generic_table.html
Normal file
41
src/main/resources/templates/fragments/generic_table.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
|
||||
<head></head>
|
||||
<body>
|
||||
<div th:fragment="table(results)">
|
||||
<div th:if="${results == null || results.isEmpty()}">
|
||||
<p class="alert alert-warning">No results.</p>
|
||||
</div>
|
||||
<div th:if="${results != null && results.size() > 0}">
|
||||
<table class="table table-striped align-middle mt-3">
|
||||
<tr class="table-data-row">
|
||||
<th th:each="field : ${results.getSortedFields()}">
|
||||
<div class="m-0 p-0 d-flex justify-content-between">
|
||||
<div class="column-title">
|
||||
<span th:if="${field.isPrimaryKey()}">
|
||||
<i title="Primary Key" class="bi bi-key"></i>
|
||||
</span>
|
||||
<span th:if="${field.isForeignKey()}">
|
||||
<i title="Foreign Key" class="bi bi-link"></i>
|
||||
</span>
|
||||
<span class="m-0 p-0" th:text="${field.getName()}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="m-0 p-0 dbfieldtype"><small th:text="${field.getType()}"></small></p>
|
||||
</th>
|
||||
</tr>
|
||||
<th:block th:each="r : ${results.getRows()}">
|
||||
<tr th:replace="~{fragments/generic_data_row :: data_row(row=${r})}">
|
||||
</tr>
|
||||
</th:block>
|
||||
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user