Skip to content
Snippets Groups Projects
Commit 012d907b authored by Moritz Staudinger's avatar Moritz Staudinger
Browse files

added mapping for escaped strings, allow re-execution again

parent 82f2a013
No related branches found
No related tags found
2 merge requests!81New stable release,!43Merge dev to master
......@@ -10,6 +10,7 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import lombok.extern.log4j.Log4j2;
import net.sf.jsqlparser.JSQLParserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
......@@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.sql.SQLException;
import java.time.Instant;
@Log4j2
......@@ -101,14 +103,10 @@ public class QueryEndpoint {
@NotNull @PathVariable("databaseId") Long databaseId,
@NotNull @PathVariable("queryId") Long queryId)
throws QueryStoreException, QueryNotFoundException, DatabaseNotFoundException, ImageNotSupportedException,
TableNotFoundException, QueryMalformedException, ContainerNotFoundException {
TableNotFoundException, QueryMalformedException, ContainerNotFoundException, SQLException, JSQLParserException {
final Query query = storeService.findOne(id, databaseId, queryId);
log.debug(query.toString());
final QueryDto queryDto = queryMapper.queryToQueryDto(query);
log.debug(queryDto.toString());
final ExecuteStatementDto statement = queryMapper.queryDtoToExecuteStatementDto(queryDto);
log.debug(statement.toString());
final QueryResultDto result = queryService.execute(id, databaseId, statement);
final QueryResultDto result = queryService.reExecute(id, databaseId, query);
result.setId(queryId);
return ResponseEntity.status(HttpStatus.ACCEPTED)
.body(result);
......
......@@ -18,6 +18,7 @@ import com.github.dockerjava.api.exception.NotModifiedException;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Network;
import lombok.extern.log4j.Log4j2;
import net.sf.jsqlparser.JSQLParserException;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
......@@ -131,7 +132,7 @@ public class QueryEndpointIntegrationTest extends BaseUnitTest {
@Test
public void reExecute_succeeds() throws TableNotFoundException, QueryStoreException, QueryMalformedException,
DatabaseNotFoundException, ImageNotSupportedException, QueryNotFoundException, InterruptedException,
SQLException, ContainerNotFoundException {
SQLException, ContainerNotFoundException, JSQLParserException {
final QueryResultDto result = QueryResultDto.builder()
.id(QUERY_1_ID)
.result(List.of(Map.of("MinTemp", 13.4, "Rainfall", 0.6, "id", 1)))
......
......@@ -295,4 +295,14 @@ public interface QueryMapper {
throw new IllegalArgumentException("Column type not known");
}
}
@Named("EscapedString")
default String stringToEscapedString(String name) throws ImageNotSupportedException {
log.debug("StringToEscapedString: {}",name);
if(name!=null && !name.startsWith("`") && !name.endsWith("`")) {
return "`"+name+"`";
}
return name;
}
}
......@@ -5,9 +5,12 @@ import at.tuwien.api.database.query.ImportDto;
import at.tuwien.api.database.query.QueryResultDto;
import at.tuwien.api.database.table.TableCsvDto;
import at.tuwien.exception.*;
import at.tuwien.querystore.Query;
import net.sf.jsqlparser.JSQLParserException;
import org.springframework.stereotype.Service;
import java.math.BigInteger;
import java.sql.SQLException;
import java.time.Instant;
@Service
......@@ -29,6 +32,23 @@ public interface QueryService {
QueryResultDto execute(Long containerId, Long databaseId, ExecuteStatementDto query) throws TableNotFoundException,
QueryStoreException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException;
/**
* Re-Executes an arbitrary query on the database container. We allow the user to only view the data, therefore the
* default "mariadb" user is allowed read-only access "SELECT".
*
* @param databaseId The database id.
* @param query The query.
* @return The result.
* @throws TableNotFoundException
* @throws QueryStoreException
* @throws QueryMalformedException
* @throws DatabaseNotFoundException
* @throws ImageNotSupportedException
*/
QueryResultDto reExecute(Long containerId, Long databaseId, Query query) throws TableNotFoundException,
QueryStoreException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException, SQLException, JSQLParserException;
/**
* Select all data known in the database-table id tuple at a given time and return a page of specific size, using
* Instant to better abstract time concept (JDK 8) from SQL. We use the "mariadb" user for this.
......
......@@ -3,7 +3,6 @@ package at.tuwien.service.impl;
import at.tuwien.InsertTableRawQuery;
import at.tuwien.api.database.query.ExecuteStatementDto;
import at.tuwien.api.database.query.ImportDto;
import at.tuwien.api.database.query.QueryDto;
import at.tuwien.api.database.query.QueryResultDto;
import at.tuwien.api.database.table.TableCsvDto;
import at.tuwien.entities.database.Database;
......@@ -16,7 +15,6 @@ import at.tuwien.repository.jpa.TableColumnRepository;
import at.tuwien.service.*;
import lombok.extern.log4j.Log4j2;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.*;
......@@ -93,7 +91,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
/* map the result to the tables (with respective columns) from the statement metadata */
final List<TableColumn> columns = parseColumns(databaseId, statement);
final QueryResultDto result = queryMapper.resultListToQueryResultDto(columns, query.getResultList());
/* Ssave query in the querystore */
/* Save query in the querystore */
Query q = storeService.insert(containerId, databaseId, result, statement, i);
result.setId(q.getId());
session.close();
......@@ -102,6 +100,43 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
return result;
}
@Override
public QueryResultDto reExecute(Long containerId, Long databaseId, Query query) throws TableNotFoundException, QueryStoreException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, ContainerNotFoundException, SQLException, JSQLParserException {
/* find */
final Database database = databaseService.find(databaseId);
if (!database.getContainer().getImage().getRepository().equals("mariadb")) {
throw new ImageNotSupportedException("Currently only MariaDB is supported");
}
/* run query */
final long startSession = System.currentTimeMillis();
final SessionFactory factory = getSessionFactory(database);
final Session session = factory.openSession();
log.debug("opened hibernate session in {} ms", System.currentTimeMillis() - startSession);
session.beginTransaction();
/* prepare the statement */
Instant i = Instant.now();
final NativeQuery<?> nativeQuery = session.createSQLQuery(queryMapper.queryToRawTimestampedQuery(query.getQuery(), database, query.getExecution()));
final int affectedTuples;
try {
log.debug("execute raw view-only query {}", query);
affectedTuples = nativeQuery.executeUpdate();
log.info("Execution on database id {} affected {} rows", databaseId, affectedTuples);
session.getTransaction()
.commit();
} catch (SQLGrammarException e) {
session.close();
factory.close();
throw new QueryMalformedException("Query not valid for this database", e);
}
/* map the result to the tables (with respective columns) from the statement metadata */
final List<TableColumn> columns = parseColumns(query, database);
final QueryResultDto result = queryMapper.resultListToQueryResultDto(columns, nativeQuery.getResultList());
/* Save query in the querystore */
session.close();
factory.close();
return result;
}
@Override
@Transactional
public QueryResultDto findAll(Long containerId, Long databaseId, Long tableId, Instant timestamp, Long page,
......@@ -270,9 +305,9 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
return columns;
}
private Query parse(Query query, Database database) throws SQLException, ImageNotSupportedException, JSQLParserException {
Instant ts = Instant.now();
query.setExecution(ts);
private List<TableColumn> parseColumns(Query query, Database database) throws SQLException, ImageNotSupportedException, JSQLParserException {
final List<TableColumn> columns = new ArrayList<>();
CCJSqlParserManager parserRealSql = new CCJSqlParserManager();
Statement statement = parserRealSql.parse(new StringReader(query.getQuery()));
log.debug("Given query {}", query.getQuery());
......@@ -298,7 +333,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
boolean i = false;
log.debug("from item iterated through: {}", f);
for(Table t : database.getTables()) {
if(f.toString().equals(t.getInternalName()) || f.toString().equals(t.getName())) {
if(queryMapper.stringToEscapedString(f.toString()).equals(queryMapper.stringToEscapedString(t.getInternalName()))) {
allColumns.addAll(t.getColumns());
i=false;
break;
......@@ -306,13 +341,14 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
i = true;
}
if(i) {
throw new JSQLParserException("Table "+f.toString() + " does not exist");
throw new JSQLParserException("Table "+queryMapper.stringToEscapedString(f.toString())+ " does not exist");
}
}
//Checking if all columns exist
for(SelectItem s : selectItems) {
String select = s.toString();
String select = queryMapper.stringToEscapedString(s.toString());
log.debug(select);
if(select.trim().equals("*")) {
log.debug("Please do not use * to query data");
continue;
......@@ -325,8 +361,9 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
boolean i = false;
for(TableColumn tc : allColumns ) {
log.debug("{},{},{}", tc.getInternalName(), tc.getName(), s);
if(select.equals(tc.getInternalName()) || select.toString().equals(tc.getName())) {
if(select.equals(queryMapper.stringToEscapedString(tc.getInternalName()))) {
i=false;
columns.add(tc);
break;
}
i = true;
......@@ -335,12 +372,7 @@ public class QueryServiceImpl extends HibernateConnector implements QueryService
throw new JSQLParserException("Column "+s.toString() + " does not exist");
}
}
//TODO Future work
if(ps.getWhere() != null) {
Expression where = ps.getWhere();
log.debug("Where clause: {}", where);
}
return query;
return columns;
}
else {
throw new JSQLParserException("SQL Query is not a SELECT statement - please only use SELECT statements");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment