Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • fair-data-austria-db-repository/fda-services
1 result
Select Git revision
Show changes
Showing
with 374 additions and 265 deletions
......@@ -4,10 +4,11 @@ import at.tuwien.api.keycloak.TokenDto;
import at.tuwien.exception.AccountNotSetupException;
import at.tuwien.exception.AuthServiceConnectionException;
import at.tuwien.exception.CredentialsInvalidException;
import at.tuwien.exception.NotAllowedException;
import org.springframework.security.authentication.BadCredentialsException;
public interface KeycloakGateway {
TokenDto obtainUserToken(String username, String password) throws AuthServiceConnectionException,
CredentialsInvalidException, AccountNotSetupException;
TokenDto obtainUserToken(String username, String password) throws BadCredentialsException;
}
......@@ -24,7 +24,7 @@ public interface MetadataServiceGateway {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
ContainerDto getContainerById(Long containerId) throws RemoteUnavailableException,
ContainerDto getContainerById(UUID containerId) throws RemoteUnavailableException,
ContainerNotFoundException, MetadataServiceException;
/**
......@@ -36,7 +36,7 @@ public interface MetadataServiceGateway {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
DatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException,
DatabaseDto getDatabaseById(UUID id) throws DatabaseNotFoundException, RemoteUnavailableException,
MetadataServiceException;
/**
......@@ -49,7 +49,7 @@ public interface MetadataServiceGateway {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
TableDto getTableById(Long databaseId, Long id) throws TableNotFoundException, RemoteUnavailableException,
TableDto getTableById(UUID databaseId, UUID id) throws TableNotFoundException, RemoteUnavailableException,
MetadataServiceException;
/**
......@@ -62,7 +62,7 @@ public interface MetadataServiceGateway {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
ViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException, ViewNotFoundException,
ViewDto getViewById(UUID databaseId, UUID id) throws RemoteUnavailableException, ViewNotFoundException,
MetadataServiceException;
/**
......@@ -86,7 +86,7 @@ public interface MetadataServiceGateway {
* @throws NotAllowedException The access to this database is denied for the given user.
* @throws MetadataServiceException The remote service returned invalid data.
*/
DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException, NotAllowedException,
DatabaseAccessDto getAccess(UUID databaseId, UUID userId) throws RemoteUnavailableException, NotAllowedException,
MetadataServiceException;
/**
......@@ -99,7 +99,7 @@ public interface MetadataServiceGateway {
* @throws DatabaseNotFoundException The database was not found.
* @throws MetadataServiceException The remote service returned invalid data.
*/
List<IdentifierBriefDto> getIdentifiers(@NotNull Long databaseId, Long subsetId) throws MetadataServiceException,
List<IdentifierBriefDto> getIdentifiers(@NotNull UUID databaseId, UUID subsetId) throws MetadataServiceException,
RemoteUnavailableException, DatabaseNotFoundException;
/**
......@@ -112,6 +112,6 @@ public interface MetadataServiceGateway {
* @throws TableNotFoundException The table was not found.
* @throws MetadataServiceException The remote service returned invalid data.
*/
void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException,
void updateTableStatistics(UUID databaseId, UUID tableId, String authorization) throws TableNotFoundException,
MetadataServiceException, RemoteUnavailableException;
}
......@@ -2,67 +2,47 @@ package at.tuwien.gateway.impl;
import at.tuwien.api.keycloak.TokenDto;
import at.tuwien.config.KeycloakConfig;
import at.tuwien.exception.AccountNotSetupException;
import at.tuwien.exception.AuthServiceConnectionException;
import at.tuwien.exception.CredentialsInvalidException;
import at.tuwien.gateway.KeycloakGateway;
import at.tuwien.mapper.MetadataMapper;
import jakarta.ws.rs.NotAuthorizedException;
import lombok.extern.log4j.Log4j2;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
@Log4j2
@Service
public class KeycloakGatewayImpl implements KeycloakGateway {
private final KeycloakConfig keycloakConfig;
private final MetadataMapper metadataMapper;
@Autowired
public KeycloakGatewayImpl(KeycloakConfig keycloakConfig) {
public KeycloakGatewayImpl(KeycloakConfig keycloakConfig, MetadataMapper metadataMapper) {
this.keycloakConfig = keycloakConfig;
this.metadataMapper = metadataMapper;
}
@Override
public TokenDto obtainUserToken(String username, String password) throws AuthServiceConnectionException,
CredentialsInvalidException, AccountNotSetupException {
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
final MultiValueMap<String, String> payload = new LinkedMultiValueMap<>();
payload.add("username", username);
payload.add("password", password);
payload.add("grant_type", "password");
payload.add("scope", "openid roles");
payload.add("client_id", keycloakConfig.getKeycloakClient());
payload.add("client_secret", keycloakConfig.getKeycloakClientSecret());
final String url = keycloakConfig.getKeycloakEndpoint() + "/realms/dbrepo/protocol/openid-connect/token";
log.trace("request user token from url: {}", url);
final ResponseEntity<TokenDto> response;
try {
response = new RestTemplate()
.exchange(url, HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class);
} catch (HttpServerErrorException e) {
public TokenDto obtainUserToken(String username, String password) throws BadCredentialsException {
try (Keycloak userKeycloak = KeycloakBuilder.builder()
.serverUrl(keycloakConfig.getKeycloakEndpoint())
.realm(keycloakConfig.getRealm())
.grantType(OAuth2Constants.PASSWORD)
.clientId(keycloakConfig.getKeycloakClient())
.clientSecret(keycloakConfig.getKeycloakClientSecret())
.username(username)
.password(password)
.build()) {
return metadataMapper.accessTokenResponseToTokenDto(userKeycloak.tokenManager()
.getAccessToken());
} catch (NotAuthorizedException e) {
log.error("Failed to obtain user token: {}", e.getMessage());
throw new AuthServiceConnectionException("Failed to obtain user token: " + e.getMessage(), e);
} catch (HttpClientErrorException.BadRequest e) {
if (e.getResponseBodyAsByteArray() != null && e.getResponseBodyAsByteArray().length > 0) {
final String error = e.getResponseBodyAsString();
if (error != null && error.contains("invalid_grant")) {
log.error("Failed to obtain user token: {}", error);
throw new AccountNotSetupException("Failed to obtain user token: " + error, e);
throw new BadCredentialsException("Failed to obtain user token", e);
}
}
log.error("Failed to obtain user token: bad request");
throw new CredentialsInvalidException("Bad request", e);
} catch (HttpClientErrorException.Unauthorized e) {
log.error("Failed to obtain user token: invalid credentials");
throw new CredentialsInvalidException("Invalid credentials: " + e.getMessage(), e);
}
return response.getBody();
}
}
......@@ -44,7 +44,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
}
@Override
public ContainerDto getContainerById(Long containerId) throws RemoteUnavailableException,
public ContainerDto getContainerById(UUID containerId) throws RemoteUnavailableException,
ContainerNotFoundException, MetadataServiceException {
final ResponseEntity<ContainerDto> response;
final String url = "/api/container/" + containerId;
......@@ -63,7 +63,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
log.error("Failed to find container with id {}: service responded unsuccessful: {}", containerId, response.getStatusCode());
throw new MetadataServiceException("Failed to find container: service responded unsuccessful: " + response.getStatusCode());
}
final List<String> expectedHeaders = List.of("X-Username", "X-Password");
final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Jdbc-Method", "X-Host", "X-Port");
if (!response.getHeaders().keySet().containsAll(expectedHeaders)) {
log.error("Failed to find all container headers");
log.debug("expected headers: {}", expectedHeaders);
......@@ -75,14 +75,17 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
throw new MetadataServiceException("Failed to find container with id " + containerId + ": body is empty");
}
final ContainerDto container = metadataMapper.containerDtoToContainerDto(response.getBody());
container.setHost(response.getHeaders().get("X-Host").get(0));
container.setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0)));
container.setUsername(response.getHeaders().get("X-Username").get(0));
container.setPassword(response.getHeaders().get("X-Password").get(0));
container.getImage().setJdbcMethod(response.getHeaders().get("X-Jdbc-Method").get(0));
container.setLastRetrieved(Instant.now());
return container;
}
@Override
public DatabaseDto getDatabaseById(Long id) throws DatabaseNotFoundException, RemoteUnavailableException,
public DatabaseDto getDatabaseById(UUID id) throws DatabaseNotFoundException, RemoteUnavailableException,
MetadataServiceException {
final ResponseEntity<DatabaseDto> response;
final String url = "/api/database/" + id;
......@@ -100,7 +103,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
log.error("Failed to find database with id {}: service responded unsuccessful: {}", id, response.getStatusCode());
throw new MetadataServiceException("Failed to find database: service responded unsuccessful: " + response.getStatusCode());
}
final List<String> expectedHeaders = List.of("X-Username", "X-Password");
final List<String> expectedHeaders = List.of("X-Username", "X-Password", "X-Jdbc-Method", "X-Host", "X-Port");
if (!response.getHeaders().keySet().containsAll(expectedHeaders)) {
log.error("Failed to find all database headers");
log.debug("expected headers: {}", expectedHeaders);
......@@ -112,14 +115,17 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
throw new MetadataServiceException("Failed to find database with id " + id + ": body is empty");
}
final DatabaseDto database = response.getBody();
database.getContainer().setHost(response.getHeaders().get("X-Host").get(0));
database.getContainer().setPort(Integer.parseInt(response.getHeaders().get("X-Port").get(0)));
database.getContainer().setUsername(response.getHeaders().get("X-Username").get(0));
database.getContainer().setPassword(response.getHeaders().get("X-Password").get(0));
database.getContainer().getImage().setJdbcMethod(response.getHeaders().get("X-Jdbc-Method").get(0));
database.setLastRetrieved(Instant.now());
return database;
}
@Override
public TableDto getTableById(Long databaseId, Long id) throws TableNotFoundException,
public TableDto getTableById(UUID databaseId, UUID id) throws TableNotFoundException,
RemoteUnavailableException, MetadataServiceException {
final ResponseEntity<TableDto> response;
final String url = "/api/database/" + databaseId + "/table/" + id;
......@@ -137,26 +143,17 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
log.error("Failed to find table with id {}: service responded unsuccessful: {}", id, response.getStatusCode());
throw new MetadataServiceException("Failed to find table: service responded unsuccessful: " + response.getStatusCode());
}
final List<String> expectedHeaders = List.of("X-Username", "X-Password");
if (!response.getHeaders().keySet().containsAll(expectedHeaders)) {
log.error("Failed to find all table headers");
log.debug("expected headers: {}", expectedHeaders);
log.debug("found headers: {}", response.getHeaders().keySet());
throw new MetadataServiceException("Failed to find all table headers");
}
if (response.getBody() == null) {
log.error("Failed to find table with id {}: body is empty", id);
throw new MetadataServiceException("Failed to find table with id " + id + ": body is empty");
}
final TableDto table = metadataMapper.tableDtoToTableDto(response.getBody());
table.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0));
table.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0));
table.setLastRetrieved(Instant.now());
return table;
}
@Override
public ViewDto getViewById(Long databaseId, Long id) throws RemoteUnavailableException,
public ViewDto getViewById(UUID databaseId, UUID id) throws RemoteUnavailableException,
ViewNotFoundException, MetadataServiceException {
final ResponseEntity<ViewDto> response;
final String url = "/api/database/" + databaseId + "/view/" + id;
......@@ -174,20 +171,11 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
log.error("Failed to find view with id {}: service responded unsuccessful: {}", id, response.getStatusCode());
throw new MetadataServiceException("Failed to find view: service responded unsuccessful: " + response.getStatusCode());
}
final List<String> expectedHeaders = List.of("X-Username", "X-Password");
if (!response.getHeaders().keySet().containsAll(expectedHeaders)) {
log.error("Failed to find all view headers");
log.debug("expected headers: {}", expectedHeaders);
log.debug("found headers: {}", response.getHeaders().keySet());
throw new MetadataServiceException("Failed to find all view headers");
}
if (response.getBody() == null) {
log.error("Failed to find view with id {}: body is empty", id);
throw new MetadataServiceException("Failed to find view with id " + id + ": body is empty");
}
final ViewDto view = metadataMapper.viewDtoToViewDto(response.getBody());
view.getDatabase().getContainer().setUsername(response.getHeaders().get("X-Username").get(0));
view.getDatabase().getContainer().setPassword(response.getHeaders().get("X-Password").get(0));
view.setLastRetrieved(Instant.now());
return view;
}
......@@ -230,7 +218,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
}
@Override
public DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException,
public DatabaseAccessDto getAccess(UUID databaseId, UUID userId) throws RemoteUnavailableException,
NotAllowedException, MetadataServiceException {
final ResponseEntity<DatabaseAccessDto> response;
final String url = "/api/database/" + databaseId + "/access/" + userId;
......@@ -256,7 +244,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
}
@Override
public List<IdentifierBriefDto> getIdentifiers(@NotNull Long databaseId, Long subsetId) throws MetadataServiceException,
public List<IdentifierBriefDto> getIdentifiers(@NotNull UUID databaseId, UUID subsetId) throws MetadataServiceException,
RemoteUnavailableException, DatabaseNotFoundException {
final ResponseEntity<IdentifierBriefDto[]> response;
final String url = "/api/identifier?dbid=" + databaseId + (subsetId != null ? ("&qid=" + subsetId) : "");
......@@ -282,7 +270,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
}
@Override
public void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException,
public void updateTableStatistics(UUID databaseId, UUID tableId, String authorization) throws TableNotFoundException,
MetadataServiceException, RemoteUnavailableException {
final ResponseEntity<Void> response;
final String url = "/api/database/" + databaseId + "/table/" + tableId + "/statistic";
......
package at.tuwien.listener;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.table.TableDto;
import at.tuwien.exception.DatabaseNotFoundException;
import at.tuwien.exception.MetadataServiceException;
import at.tuwien.exception.RemoteUnavailableException;
import at.tuwien.exception.TableNotFoundException;
import at.tuwien.service.CredentialService;
import at.tuwien.service.CacheService;
import at.tuwien.service.QueueService;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
......@@ -21,20 +23,21 @@ import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Log4j2
@Component
public class DefaultListener implements MessageListener {
private final CacheService cacheService;
private final ObjectMapper objectMapper;
private final QueueService queueService;
private final CredentialService credentialService;
@Autowired
public DefaultListener(ObjectMapper objectMapper, QueueService queueService, CredentialService credentialService) {
public DefaultListener(CacheService cacheService, ObjectMapper objectMapper, QueueService queueService) {
this.cacheService = cacheService;
this.objectMapper = objectMapper;
this.queueService = queueService;
this.credentialService = credentialService;
}
@Override
......@@ -53,20 +56,23 @@ public class DefaultListener implements MessageListener {
log.error("Failed to map database and table names from routing key: is not 3-part");
return;
}
final Long databaseId = Long.parseLong(parts[1]);
final Long tableId = Long.parseLong(parts[2]);
final UUID databaseId = UUID.fromString(parts[1]);
final UUID tableId = UUID.fromString(parts[2]);
log.trace("received message for table with id {} of database id {}: {} bytes", tableId, databaseId, message.getMessageProperties().getContentLength());
final Map<String, Object> body;
try {
final TableDto table = credentialService.getTable(databaseId, tableId);
final DatabaseDto database = cacheService.getDatabase(databaseId);
final TableDto table = cacheService.getTable(databaseId, tableId);
body = objectMapper.readValue(message.getBody(), typeRef);
queueService.insert(table, body);
queueService.insert(database, table, body);
} catch (IOException e) {
log.error("Failed to read object: {}", e.getMessage());
} catch (SQLException | RemoteUnavailableException e) {
log.error("Failed to insert tuple: {}", e.getMessage());
} catch (TableNotFoundException | MetadataServiceException e) {
log.error("Failed to find table: {}", e.getMessage());
} catch (DatabaseNotFoundException e) {
log.error("Failed to find database: {}", e.getMessage());
}
}
}
......@@ -4,14 +4,8 @@ import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewColumnDto;
import at.tuwien.api.database.ViewDto;
import at.tuwien.api.database.query.QueryDto;
import at.tuwien.api.database.table.TableBriefDto;
import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.database.table.TableHistoryDto;
import at.tuwien.api.database.table.TableStatisticDto;
import at.tuwien.api.database.table.columns.ColumnBriefDto;
import at.tuwien.api.database.table.columns.ColumnDto;
import at.tuwien.api.database.table.columns.ColumnStatisticDto;
import at.tuwien.api.database.table.columns.ColumnTypeDto;
import at.tuwien.api.database.table.*;
import at.tuwien.api.database.table.columns.*;
import at.tuwien.api.database.table.constraints.ConstraintsDto;
import at.tuwien.api.database.table.constraints.foreign.ForeignKeyBriefDto;
import at.tuwien.api.database.table.constraints.foreign.ForeignKeyDto;
......@@ -20,7 +14,6 @@ import at.tuwien.api.database.table.constraints.foreign.ReferenceTypeDto;
import at.tuwien.api.database.table.constraints.primary.PrimaryKeyDto;
import at.tuwien.api.database.table.constraints.unique.UniqueDto;
import at.tuwien.api.user.UserBriefDto;
import at.tuwien.config.QueryConfig;
import at.tuwien.exception.TableNotFoundException;
import org.apache.hadoop.shaded.com.google.common.hash.Hashing;
import org.apache.hadoop.shaded.org.apache.commons.io.FileUtils;
......@@ -55,9 +48,6 @@ public interface DataMapper {
ColumnBriefDto columnDtoToColumnBriefDto(ColumnDto data);
/* redundant */
@Mappings({
@Mapping(target = "databaseId", source = "tdbid")
})
TableBriefDto tableDtoToTableBriefDto(TableDto data);
/* redundant */
......@@ -65,9 +55,11 @@ public interface DataMapper {
ForeignKeyBriefDto foreignKeyDtoToForeignKeyBriefDto(ForeignKeyDto data);
default String rabbitMqTupleToInsertOrUpdateQuery(TableDto table, Map<String, Object> data) {
default String rabbitMqTupleToInsertOrUpdateQuery(String databaseName, TableDto table, Map<String, Object> data) {
/* parameterized query for prepared statement */
final StringBuilder statement = new StringBuilder("INSERT INTO `")
.append(databaseName)
.append("`.`")
.append(table.getInternalName())
.append("` (")
.append(data.keySet()
......@@ -96,7 +88,7 @@ public interface DataMapper {
return ViewDto.builder()
.name(resultSet.getString(1))
.internalName(resultSet.getString(1))
.vdbid(database.getId())
.databaseId(database.getId())
.isInitialView(false)
.isPublic(database.getIsPublic())
.isSchemaPublic(database.getIsSchemaPublic())
......@@ -136,7 +128,7 @@ public interface DataMapper {
.name(resultSet.getString(10))
.internalName(resultSet.getString(10))
.tableId(table.getId())
.databaseId(table.getTdbid())
.databaseId(table.getDatabaseId())
.description(resultSet.getString(11))
.build();
final String dataType = resultSet.getString(8);
......@@ -144,14 +136,18 @@ public interface DataMapper {
column.setEnums(Arrays.stream(dataType.substring(0, resultSet.getString(8).length() - 1)
.replace("enum(", "")
.split(","))
.map(value -> value.replace("'", ""))
.map(value -> EnumDto.builder()
.value(value.replace("'", ""))
.build())
.toList());
}
if (column.getColumnType().equals(ColumnTypeDto.SET)) {
column.setSets(Arrays.stream(dataType.substring(0, dataType.length() - 1)
.replace("set(", "")
.split(","))
.map(value -> value.replace("'", ""))
.map(value -> SetDto.builder()
.value(value.replace("'", ""))
.build())
.toList());
}
/* fix boolean and set size for others */
......@@ -174,7 +170,7 @@ public interface DataMapper {
return table;
}
default ViewDto resultSetToTable(ResultSet resultSet, ViewDto view, QueryConfig queryConfig) throws SQLException {
default ViewDto resultSetToTable(ResultSet resultSet, ViewDto view) throws SQLException {
final ViewColumnDto column = ViewColumnDto.builder()
.ordinalPosition(resultSet.getInt(1) - 1) /* start at zero */
.isNullAllowed(resultSet.getString(3).equals("YES"))
......@@ -182,7 +178,7 @@ public interface DataMapper {
.d(resultSet.getString(7) != null ? resultSet.getLong(7) : null)
.name(resultSet.getString(10))
.internalName(resultSet.getString(10))
.databaseId(view.getVdbid())
.databaseId(view.getDatabaseId())
.build();
/* fix boolean and set size for others */
if (resultSet.getString(8).equalsIgnoreCase("tinyint(1)")) {
......@@ -194,14 +190,13 @@ public interface DataMapper {
}
view.getColumns()
.add(column);
log.trace("parsed view column: {}.{}", view.getInternalName(), column.getInternalName());
return view;
}
default QueryDto resultSetToQueryDto(@NotNull ResultSet data) throws SQLException {
/* note that next() is called outside this mapping function */
final QueryDto subset = QueryDto.builder()
.id(data.getLong(1))
.id(UUID.fromString(data.getString(1)))
.query(data.getString(4))
.queryHash(data.getString(5))
.resultHash(data.getString(6))
......@@ -227,7 +222,7 @@ public interface DataMapper {
.timestamp(LocalDateTime.parse(resultSet.getString(1), mariaDbFormatter)
.atZone(ZoneId.of("UTC"))
.toInstant())
.event(resultSet.getString(2))
.event(HistoryEventTypeDto.valueOf(resultSet.getString(2).toUpperCase()))
.total(resultSet.getLong(3))
.build());
}
......@@ -277,12 +272,12 @@ public interface DataMapper {
.column(ColumnBriefDto.builder()
.name(columnName)
.internalName(columnName)
.databaseId(table.getTdbid())
.databaseId(table.getDatabaseId())
.build())
.referencedColumn(ColumnBriefDto.builder()
.name(referencedColumnName)
.internalName(referencedColumnName)
.databaseId(table.getTdbid())
.databaseId(table.getDatabaseId())
.build())
.build();
if (optional1.isPresent()) {
......@@ -299,7 +294,7 @@ public interface DataMapper {
.referencedTable(TableBriefDto.builder()
.name(referencedTable)
.internalName(referencedTable)
.databaseId(table.getTdbid())
.databaseId(table.getDatabaseId())
.build())
.references(new LinkedList<>(List.of(foreignKeyReference)))
.onDelete(deleteRule)
......@@ -329,7 +324,7 @@ public interface DataMapper {
.avgRowLength(resultSet.getLong(4))
.dataLength(resultSet.getLong(5))
.maxDataLength(resultSet.getLong(6))
.tdbid(database.getId())
.databaseId(database.getId())
.queueName("dbrepo")
.routingKey("dbrepo")
.description(resultSet.getString(10))
......
package at.tuwien.mapper;
import at.tuwien.api.container.image.OperatorDto;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.query.FilterDto;
import at.tuwien.api.database.query.FilterTypeDto;
import at.tuwien.api.database.query.OrderDto;
import at.tuwien.api.database.query.SubsetDto;
import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.database.table.TupleDeleteDto;
import at.tuwien.api.database.table.TupleDto;
......@@ -7,9 +13,14 @@ import at.tuwien.api.database.table.TupleUpdateDto;
import at.tuwien.api.database.table.columns.ColumnDto;
import at.tuwien.api.database.table.columns.ColumnTypeDto;
import at.tuwien.api.database.table.columns.CreateTableColumnDto;
import at.tuwien.exception.ImageNotFoundException;
import at.tuwien.exception.QueryMalformedException;
import at.tuwien.exception.TableMalformedException;
import at.tuwien.exception.TableNotFoundException;
import at.tuwien.utils.MariaDbUtil;
import org.jooq.Record;
import org.jooq.*;
import org.jooq.conf.ParamType;
import org.mapstruct.Mapper;
import org.mapstruct.Named;
......@@ -27,6 +38,9 @@ import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.select;
@Mapper(componentModel = "spring", uses = {MetadataMapper.class, DataMapper.class})
public interface MariaDbMapper {
......@@ -112,14 +126,8 @@ public interface MariaDbMapper {
return statement.toString();
}
default String queryStoreCreateSequenceRawQuery() {
final String statement = "CREATE SEQUENCE `qs_queries_seq` NOCACHE;";
log.trace("mapped create query store sequence statement: {}", statement);
return statement;
}
default String queryStoreCreateTableRawQuery() {
final String statement = "CREATE TABLE `qs_queries` ( `id` bigint not null primary key default nextval(`qs_queries_seq`), `created` datetime not null default now(), `executed` datetime not null default now(), `created_by` varchar(36), `query` text not null, `query_normalized` text not null, `is_persisted` boolean not null, `query_hash` varchar(255) not null, `result_hash` varchar(255), `result_number` bigint) WITH SYSTEM VERSIONING;";
final String statement = "CREATE TABLE `qs_queries` ( `id` VARCHAR(36) NOT NULL PRIMARY KEY DEFAULT UUID(), `created` datetime NOT NULL DEFAULT NOW(), `executed` datetime NOT NULL default now(), `created_by` VARCHAR(36), `query` text NOT NULL, `query_normalized` text NOT NULL, `is_persisted` boolean NOT NULL, `query_hash` VARCHAR(255) NOT NULL, `result_hash` VARCHAR(255), `result_number` bigint) WITH SYSTEM VERSIONING;";
log.trace("mapped create query store table statement: {}", statement);
return statement;
}
......@@ -131,13 +139,13 @@ public interface MariaDbMapper {
}
default String queryStoreCreateStoreQueryProcedureRawQuery() {
final String statement = "CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _username varchar(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;";
final String statement = "CREATE PROCEDURE store_query(IN query TEXT, IN executed DATETIME, OUT queryId VARCHAR(36)) BEGIN DECLARE _queryhash VARCHAR(255) DEFAULT SHA2(query, 256); DECLARE _username VARCHAR(255) DEFAULT REGEXP_REPLACE(current_user(), '@.*', ''); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;";
log.trace("mapped create query store store_query procedure statement: {}", statement);
return statement;
}
default String queryStoreCreateInternalStoreQueryProcedureRawQuery() {
final String statement = "CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId BIGINT) BEGIN DECLARE _queryhash varchar(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;";
final String statement = "CREATE DEFINER = 'root' PROCEDURE _store_query(IN _username VARCHAR(255), IN query TEXT, IN executed DATETIME, OUT queryId VARCHAR(36)) BEGIN DECLARE _queryhash VARCHAR(255) DEFAULT SHA2(query, 256); DECLARE _query TEXT DEFAULT CONCAT('CREATE OR REPLACE TABLE _tmp AS (', query, ')'); PREPARE stmt FROM _query; EXECUTE stmt; DEALLOCATE PREPARE stmt; CALL hash_table('_tmp', @hash, @count); DROP TABLE IF EXISTS `_tmp`; IF @hash IS NULL THEN INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` IS NULL); ELSE INSERT INTO `qs_queries` (`created_by`, `query`, `query_normalized`, `is_persisted`, `query_hash`, `result_hash`, `result_number`, `executed`) SELECT _username, query, query, false, _queryhash, @hash, @count, executed WHERE NOT EXISTS (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); SET queryId = (SELECT `id` FROM `qs_queries` WHERE `query_hash` = _queryhash AND `result_hash` = @hash); END IF; END;";
log.trace("mapped create query store _store_query procedure statement: {}", statement);
return statement;
}
......@@ -179,10 +187,14 @@ public interface MariaDbMapper {
}
@Named("dropView")
default String dropViewRawQuery(String viewName) {
final String statement = "DROP VIEW IF EXISTS `" + viewName + "`;";
default String dropViewRawQuery(String databaseName, String viewName) {
final StringBuilder statement = new StringBuilder("DROP VIEW IF EXISTS `")
.append(databaseName)
.append("`.`")
.append(viewName)
.append("`;");
log.trace("mapped drop view statement: {}", statement);
return statement;
return statement.toString();
}
default String databaseViewSelectRawQuery() {
......@@ -295,19 +307,24 @@ public interface MariaDbMapper {
return statement.toString();
}
default String tableNameToUpdateTableRawQuery(String internalName) {
default String tableNameToUpdateTableRawQuery(String databaseName, String internalName) {
final StringBuilder stringBuilder = new StringBuilder("ALTER TABLE `")
.append(databaseName)
.append("`.`")
.append(internalName)
.append("` COMMENT = ?;");
log.trace("mapped update table statement: {}", stringBuilder);
return stringBuilder.toString();
}
default String tableCreateDtoToCreateTableRawQuery(at.tuwien.api.database.table.internal.TableCreateDto data) {
default String tableCreateDtoToCreateTableRawQuery(String databaseName,
at.tuwien.api.database.table.internal.TableCreateDto data) {
final StringBuilder stringBuilder = new StringBuilder("CREATE TABLE `")
.append(databaseName)
.append("`.`")
.append(nameToInternalName(data.getName()))
.append("` (");
log.trace("primary key column(s) exist: {}", data.getConstraints().getPrimaryKey());
log.trace("PRIMARY KEY column(s) exist: {}", data.getConstraints().getPrimaryKey());
final int[] idx = {0};
for (CreateTableColumnDto column : data.getColumns()) {
stringBuilder.append(idx[0]++ > 0 ? ", " : "")
......@@ -326,11 +343,11 @@ public interface MariaDbMapper {
}
}
/* create primary key index */
/* create PRIMARY KEY index */
if (data.getConstraints() != null) {
log.trace("constraints are {}", data.getConstraints());
if (data.getConstraints().getPrimaryKey() != null && !data.getConstraints().getPrimaryKey().isEmpty()) {
/* create primary key index */
/* create PRIMARY KEY index */
stringBuilder.append(", PRIMARY KEY (")
.append(String.join(",", data.getConstraints()
.getPrimaryKey()
......@@ -475,16 +492,18 @@ public interface MariaDbMapper {
}
@Named("dropTableQuery")
default String dropTableRawQuery(String tableName) {
return dropTableRawQuery(tableName, true);
default String dropTableRawQuery(String databaseName, String tableName) {
return dropTableRawQuery(databaseName, tableName, true);
}
default String dropTableRawQuery(String tableName, Boolean force) {
default String dropTableRawQuery(String databaseName, String tableName, Boolean force) {
final StringBuilder statement = new StringBuilder("DROP TABLE ");
if (!force) {
statement.append("IF EXISTS ");
}
statement.append("`")
.append(databaseName)
.append("`.`")
.append(tableName)
.append("`;");
log.trace("mapped drop table query: {}", statement);
......@@ -513,13 +532,15 @@ public interface MariaDbMapper {
return statement.toString();
}
default String tupleToRawDeleteQuery(TableDto table, TupleDeleteDto data) throws TableMalformedException {
default String tupleToRawDeleteQuery(String databaseName, TableDto table, TupleDeleteDto data) throws TableMalformedException {
log.trace("table csv to delete query, table.id={}, data.keys={}", table.getId(), data.getKeys());
if (table.getColumns().isEmpty()) {
throw new TableMalformedException("Columns are not known");
}
/* parameterized query for prepared statement */
final StringBuilder statement = new StringBuilder("DELETE FROM `")
.append(databaseName)
.append("`.`")
.append(table.getInternalName())
.append("` WHERE ");
final int[] idx = new int[]{0};
......@@ -534,14 +555,14 @@ public interface MariaDbMapper {
return statement.toString();
}
default String tupleToRawUpdateQuery(TableDto table, TupleUpdateDto data)
default String tupleToRawUpdateQuery(String databaseName, TableDto table, TupleUpdateDto data)
throws TableMalformedException {
if (table.getColumns().isEmpty()) {
throw new TableMalformedException("Columns are not known");
}
/* parameterized query for prepared statement */
final StringBuilder statement = new StringBuilder("UPDATE `")
.append(table.getDatabase().getInternalName())
.append(databaseName)
.append("`.`")
.append(table.getInternalName())
.append("` SET ");
......@@ -573,13 +594,13 @@ public interface MariaDbMapper {
return statement.toString();
}
default String tupleToRawCreateQuery(TableDto table, TupleDto data) throws TableMalformedException {
default String tupleToRawCreateQuery(String databaseName, TableDto table, TupleDto data) throws TableMalformedException {
if (table.getColumns().isEmpty()) {
throw new TableMalformedException("Columns are not known");
}
/* parameterized query for prepared statement */
final StringBuilder statement = new StringBuilder("INSERT INTO `")
.append(table.getDatabase().getInternalName())
.append(databaseName)
.append("`.`")
.append(table.getInternalName())
.append("` (");
......@@ -794,4 +815,160 @@ public interface MariaDbMapper {
return statement.toString();
}
default SelectConditionStep<Record> subsetDtoToSelectConditions(SelectJoinStep<Record> step, DatabaseDto database,
SubsetDto data) throws TableNotFoundException,
ImageNotFoundException {
if (data.getFilter() == null || data.getFilter().isEmpty()) {
return step.where();
}
SelectConditionStep<Record> conditions = step.where();
FilterTypeDto next = null;
for (int i = 0; i < data.getFilter().size(); i++) {
final FilterDto filter = data.getFilter().get(i);
final ColumnDto column = columnIdToColumnDto(database, filter.getColumnId());
if (i == 0) {
conditions = step.where(filterDtoToCondition(database, column, filter));
} else if (next != null) {
if (next.equals(FilterTypeDto.OR)) {
conditions = conditions.or(filterDtoToCondition(database, column, filter));
} else if (next.equals(FilterTypeDto.AND)) {
conditions = conditions.and(filterDtoToCondition(database, column, filter));
}
}
next = filter.getType();
}
return conditions;
}
default Condition filterDtoToCondition(DatabaseDto database, ColumnDto column, FilterDto data)
throws ImageNotFoundException {
final String operator = operatorIdToOperatorDto(database, data.getOperatorId()).getValue();
switch (operator) {
case "=":
case "<=>":
return field(column.getInternalName()).eq(data.getValue());
case "<":
return field(column.getInternalName()).lt(data.getValue());
case "<=":
return field(column.getInternalName()).le(data.getValue());
case ">":
return field(column.getInternalName()).gt(data.getValue());
case ">=":
return field(column.getInternalName()).ge(data.getValue());
case "!=":
return field(column.getInternalName()).ne(data.getValue());
case "LIKE":
return field(column.getInternalName()).like(data.getValue());
case "NOT LIKE":
return field(column.getInternalName()).notLike(data.getValue());
case "IN":
return field(column.getInternalName()).in(data.getValue());
case "NOT IN":
return field(column.getInternalName()).notIn(data.getValue());
case "IS NOT NULL":
return field(column.getInternalName()).isNotNull();
case "IS NULL":
return field(column.getInternalName()).isNull();
case "REGEXP":
return field(column.getInternalName()).likeRegex(data.getValue());
case "NOT REGEXP":
return field(column.getInternalName()).notLikeRegex(data.getValue());
}
log.error("Failed to map operator: {}", operator);
throw new IllegalArgumentException("Failed to map operator: " + operator);
}
default SelectSeekStepN<Record> subsetDtoToSelectOrder(SelectConditionStep<Record> step, DatabaseDto database,
SubsetDto data) throws TableNotFoundException {
final List<OrderField<Object>> sort = new LinkedList<>();
for (OrderDto order : data.getOrder()) {
final ColumnDto column = columnIdToColumnDto(database, order.getColumnId());
if (order.getDirection() == null) {
sort.add(field(column.getInternalName()));
continue;
}
switch (order.getDirection()) {
case ASC -> sort.add(field(column.getInternalName()).asc());
case DESC -> sort.add(field(column.getInternalName()).desc());
}
}
return step.orderBy(sort);
}
default String subsetDtoToRawQuery(DatabaseDto database, SubsetDto data) throws TableNotFoundException, ImageNotFoundException {
final TableDto table = tableIdToTableDto(database, data.getTableId());
final List<Field<Object>> columns = table.getColumns()
.stream()
.filter(c -> data.getColumns().contains(c.getId()))
.map(c -> field(c.getInternalName()))
.toList();
final SelectJoinStep<Record> query = select(columns)
.from(table.getInternalName());
final SelectConditionStep<Record> where = subsetDtoToSelectConditions(query, database, data);
final String sql;
if (data.getOrder() == null) {
sql = where.getSQL(ParamType.INLINED);
} else {
sql = subsetDtoToSelectOrder(where, database, data)
.getSQL(ParamType.INLINED);
}
log.trace("mapped prepared query: {}", sql);
return sql;
}
default ColumnDto columnIdToColumnDto(TableDto table, UUID columnId) throws TableNotFoundException {
final Optional<ColumnDto> optional = table.getColumns()
.stream()
.filter(c -> c.getId().equals(columnId))
.findFirst();
if (optional.isEmpty()) {
log.error("Failed to find filtered column with id: {}", columnId);
throw new TableNotFoundException("Failed to find filtered column");
}
return optional.get();
}
default ColumnDto columnIdToColumnDto(DatabaseDto database, UUID columnId) throws TableNotFoundException {
if (columnId == null) {
return null;
}
final Optional<ColumnDto> optional = database.getTables()
.stream()
.map(TableDto::getColumns)
.flatMap(List::stream)
.filter(column -> column.getId().equals(columnId))
.findFirst();
if (optional.isEmpty()) {
log.error("Failed to find column: {}", columnId);
throw new TableNotFoundException("Failed to find column");
}
return optional.get();
}
default OperatorDto operatorIdToOperatorDto(DatabaseDto database, UUID operatorId) throws ImageNotFoundException {
final Optional<OperatorDto> optional = database.getContainer()
.getImage()
.getOperators()
.stream()
.filter(op -> op.getId().equals(operatorId))
.findFirst();
if (optional.isEmpty()) {
log.error("Failed to find operator with id: {}", operatorId);
throw new ImageNotFoundException("Failed to find operator");
}
return optional.get();
}
default TableDto tableIdToTableDto(DatabaseDto database, UUID tableId) throws TableNotFoundException {
final Optional<TableDto> optional = database.getTables()
.stream()
.filter(t -> t.getId().equals(tableId))
.findFirst();
if (optional.isEmpty()) {
log.error("Failed to find table with id: {}", tableId);
throw new TableNotFoundException("Failed to find table");
}
return optional.get();
}
}
......@@ -12,12 +12,16 @@ import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.database.table.columns.ColumnDto;
import at.tuwien.api.identifier.IdentifierBriefDto;
import at.tuwien.api.identifier.IdentifierDto;
import at.tuwien.api.keycloak.TokenDto;
import at.tuwien.api.user.UserBriefDto;
import at.tuwien.api.user.UserDto;
import org.keycloak.representations.AccessTokenResponse;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import java.util.UUID;
@Mapper(componentModel = "spring", imports = {DatabaseDto.class, ContainerDto.class, ImageDto.class})
public interface MetadataMapper {
......@@ -43,16 +47,18 @@ public interface MetadataMapper {
UserDto userDtoToUserDto(UserDto data);
UserBriefDto userDtoToUserBriefDto(UserDto data);
@Mappings({
@Mapping(target = "databaseId", source = "tdbid")
@Mapping(target = "accessToken", source = "token")
})
TokenDto accessTokenResponseToTokenDto(AccessTokenResponse data);
UserBriefDto userDtoToUserBriefDto(UserDto data);
TableBriefDto tableDtoToTableBriefDto(TableDto data);
IdentifierBriefDto identifierDtoToIdentifierBriefDto(IdentifierDto data);
default String metricToUri(String baseUrl, Long databaseId, Long tableId, Long subsetId, Long viewId) {
default String metricToUri(String baseUrl, UUID databaseId, UUID tableId, UUID subsetId, UUID viewId) {
final StringBuilder uri = new StringBuilder(baseUrl)
.append("/database/")
.append(databaseId);
......
......@@ -10,7 +10,7 @@ import at.tuwien.exception.*;
import java.util.UUID;
public interface CredentialService {
public interface CacheService {
/**
* Gets credentials for a database with given id either from the cache (if not expired) or retrieves them from the
......@@ -22,7 +22,7 @@ public interface CredentialService {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
DatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException,
DatabaseDto getDatabase(UUID id) throws DatabaseNotFoundException, RemoteUnavailableException,
MetadataServiceException;
/**
......@@ -35,7 +35,7 @@ public interface CredentialService {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
ContainerDto getContainer(Long id) throws ContainerNotFoundException, RemoteUnavailableException,
ContainerDto getContainer(UUID id) throws ContainerNotFoundException, RemoteUnavailableException,
MetadataServiceException;
/**
......@@ -49,7 +49,7 @@ public interface CredentialService {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
TableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException,
TableDto getTable(UUID databaseId, UUID tableId) throws RemoteUnavailableException,
MetadataServiceException, TableNotFoundException;
/**
......@@ -63,7 +63,7 @@ public interface CredentialService {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
ViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException,
ViewDto getView(UUID databaseId, UUID viewId) throws RemoteUnavailableException,
MetadataServiceException, ViewNotFoundException;
/**
......@@ -90,13 +90,6 @@ public interface CredentialService {
* @throws RemoteUnavailableException The remote service is not available.
* @throws MetadataServiceException The remote service returned invalid data.
*/
DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException,
DatabaseAccessDto getAccess(UUID databaseId, UUID userId) throws RemoteUnavailableException,
MetadataServiceException, NotAllowedException;
/**
* Invalidate the caches to force a refresh of access to a database with given id.
*
* @param databaseId The database id.
*/
void invalidateAccess(Long databaseId);
}
package at.tuwien.service;
import at.tuwien.api.database.CreateViewDto;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewDto;
import at.tuwien.api.database.table.TableDto;
......@@ -40,12 +39,13 @@ public interface DatabaseService {
/**
* Creates a view in given data database with view definition.
* @param database The data database object.
* @param data The view definition.
* @param viewName The view name.
* @param query The view query.
* @return The generated view.
* @throws SQLException
* @throws ViewMalformedException
*/
ViewDto createView(DatabaseDto database, CreateViewDto data) throws SQLException,
ViewDto createView(DatabaseDto database, String viewName, String query) throws SQLException,
ViewMalformedException;
/**
......
package at.tuwien.service;
public interface MetricsService {
void countTableGetData(Long databaseId, Long tableId);
void countSubsetGetData(Long databaseId, Long subsetId);
void countViewGetData(Long databaseId, Long viewId);
}
package at.tuwien.service;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.table.TableDto;
import java.sql.SQLException;
......@@ -10,9 +11,10 @@ public interface QueueService {
/**
* Inserts data into the table of a given database.
*
* @param database The database.
* @param table The table.
* @param data The data.
* @throws SQLException The connection to the database could not be established.
*/
void insert(TableDto table, Map<String, Object> data) throws SQLException;
void insert(DatabaseDto database, TableDto table, Map<String, Object> data) throws SQLException;
}
......@@ -13,6 +13,8 @@ import java.util.List;
public interface StorageService {
String putObject(byte[] content);
/**
* Loads an object of a bucket from the Storage Service into an input stream.
*
......
package at.tuwien.service;
import at.tuwien.api.SortTypeDto;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.query.QueryDto;
import at.tuwien.api.database.query.SubsetDto;
import at.tuwien.exception.*;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
......@@ -18,15 +18,15 @@ public interface SubsetService {
* Creates a subset from the given statement at given time in the given database.
*
* @param database The database.
* @param statement The subset statement.
* @param subset The subset information.
* @param timestamp The timestamp as of which the data is queried. If smaller than <now>, historic data is queried.
* @param userId The user id of the creating user.
* @return The query id.
* @throws QueryStoreInsertException The query store refused to insert the query.
* @throws SQLException The connection to the database could not be established.
*/
Long create(DatabaseDto database, String statement, Instant timestamp, UUID userId)
throws QueryStoreInsertException, SQLException;
UUID create(DatabaseDto database, SubsetDto subset, Instant timestamp, UUID userId)
throws QueryStoreInsertException, SQLException, QueryMalformedException, TableNotFoundException, ImageNotFoundException, ViewMalformedException;
/**
* Counts the subset row count of a query of a given subset in the given database.
......@@ -34,26 +34,21 @@ public interface SubsetService {
* @param database The database.
* @param query The subset.
* @return The row count.
* @throws TableMalformedException The table is malformed.
* @throws QueryMalformedException The query is malformed.
* @throws SQLException The connection to the database could not be established.
*/
Long reExecuteCount(DatabaseDto database, QueryDto query) throws TableMalformedException,
SQLException, QueryMalformedException;
Long reExecuteCount(DatabaseDto database, QueryDto query) throws SQLException, QueryMalformedException;
/**
* Retrieve data from a subset in a database and optionally paginate with number of page and size of results.
*
* @param database The database.
* @param query The query statements.
* @param page The page number.
* @param size Te result size.
* @return The data.
* @throws QueryMalformedException The mapped query produced a database error.
* @throws TableNotFoundException The database table is malformed.
*/
Dataset<Row> getData(DatabaseDto database, String query, Instant timestamp, Long page, Long size,
SortTypeDto sortDirection, String sortColumn) throws QueryMalformedException,
TableNotFoundException;
Dataset<Row> getData(DatabaseDto database, String query) throws QueryMalformedException, TableNotFoundException;
/**
* Finds all queries in the query store of the given database id and query id.
......@@ -79,10 +74,9 @@ public interface SubsetService {
* @return The row count.
* @throws SQLException The connection to the database could not be established.
* @throws QueryMalformedException The mapped query produced a database error.
* @throws TableMalformedException The database table is malformed.
*/
Long executeCountNonPersistent(DatabaseDto database, String statement, Instant timestamp)
throws SQLException, QueryMalformedException, TableMalformedException;
throws SQLException, QueryMalformedException;
/**
* Finds a query in the query store of the given database id and query id.
......@@ -97,21 +91,21 @@ public interface SubsetService {
* @throws DatabaseNotFoundException The database metadata was not found in the Metadata Service.
* @throws MetadataServiceException Communication with the Metadata Service failed.
*/
QueryDto findById(DatabaseDto database, Long queryId) throws QueryNotFoundException, SQLException,
QueryDto findById(DatabaseDto database, UUID queryId) throws QueryNotFoundException, SQLException,
RemoteUnavailableException, UserNotFoundException, DatabaseNotFoundException, MetadataServiceException;
/**
* Inserts a query and metadata to the query store of a given database id.
*
* @param database The database.
* @param query The query statement.
* @param statement The query statement.
* @param userId The user id.
* @return The stored query id on success.
* @throws SQLException The connection to the database could not be established.
* @throws QueryStoreInsertException The query store failed to insert the query.
*/
Long storeQuery(DatabaseDto database, String query, Instant timestamp, UUID userId) throws SQLException,
QueryStoreInsertException;
UUID storeQuery(DatabaseDto database, String statement, Instant timestamp, UUID userId) throws SQLException,
QueryStoreInsertException, ViewMalformedException;
/**
* Persists a query to be displayed in the frontend.
......@@ -122,7 +116,7 @@ public interface SubsetService {
* @throws SQLException The connection to the database could not be established.
* @throws QueryStorePersistException The query store failed to persist/unpersist the query.
*/
void persist(DatabaseDto database, Long queryId, Boolean persist) throws SQLException,
void persist(DatabaseDto database, UUID queryId, Boolean persist) throws SQLException,
QueryStorePersistException;
/**
......
package at.tuwien.service;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.query.ImportDto;
import at.tuwien.api.database.table.*;
import at.tuwien.exception.*;
......@@ -19,7 +20,7 @@ public interface TableService {
* @throws TableMalformedException The table statistic generation was unsuccessful, likely due to a bug in the mapping.
* @throws TableNotFoundException The table could not be inspected in the data database.
*/
TableStatisticDto getStatistics(TableDto table) throws SQLException, TableMalformedException,
TableStatisticDto getStatistics(DatabaseDto database, TableDto table) throws SQLException, TableMalformedException,
TableNotFoundException;
/**
......@@ -31,7 +32,7 @@ public interface TableService {
* @throws TableMalformedException The table schema is malformed.
* @throws TableNotFoundException The table could not be inspected in the metadata database.
*/
void updateTable(TableDto table, TableUpdateDto data) throws SQLException,
void updateTable(DatabaseDto database, TableDto table, TableUpdateDto data) throws SQLException,
TableMalformedException, TableNotFoundException;
/**
......@@ -41,7 +42,7 @@ public interface TableService {
* @throws SQLException Failed to parse SQL query, contains invalid syntax.
* @throws QueryMalformedException The drop table query is malformed.
*/
void delete(TableDto table) throws SQLException, QueryMalformedException;
void delete(DatabaseDto database, TableDto table) throws SQLException, QueryMalformedException;
/**
* Obtains the table history for a given table object.
......@@ -52,7 +53,7 @@ public interface TableService {
* @throws SQLException Failed to parse SQL query, contains invalid syntax.
* @throws TableNotFoundException The table could not be found in the data database.
*/
List<TableHistoryDto> history(TableDto table, Long size) throws SQLException, TableNotFoundException;
List<TableHistoryDto> history(DatabaseDto database, TableDto table, Long size) throws SQLException, TableNotFoundException;
/**
* Obtains the table data tuples count at time.
......@@ -63,7 +64,7 @@ public interface TableService {
* @throws SQLException Failed to parse SQL query, contains invalid syntax.
* @throws QueryMalformedException The count query is malformed, likely due to a bug in the application.
*/
Long getCount(TableDto table, Instant timestamp) throws SQLException,
Long getCount(DatabaseDto database, TableDto table, Instant timestamp) throws SQLException,
QueryMalformedException;
/**
......@@ -78,7 +79,7 @@ public interface TableService {
* @throws SQLException Failed to parse SQL query, contains invalid syntax.
* @throws QueryMalformedException The import query is malformed, likely due to a bug in the application.
*/
void importDataset(TableDto table, ImportDto data) throws MalformedException, StorageNotFoundException,
void importDataset(DatabaseDto database, TableDto table, ImportDto data) throws MalformedException, StorageNotFoundException,
StorageUnavailableException, SQLException, QueryMalformedException, TableMalformedException;
/**
......@@ -90,7 +91,7 @@ public interface TableService {
* @throws TableMalformedException The tuple is malformed and does not fit the table schema.
* @throws QueryMalformedException The delete query is malformed, likely due to a bug in the application.
*/
void deleteTuple(TableDto table, TupleDeleteDto data) throws SQLException,
void deleteTuple(DatabaseDto database, TableDto table, TupleDeleteDto data) throws SQLException,
TableMalformedException, QueryMalformedException;
/**
......@@ -104,7 +105,7 @@ public interface TableService {
* @throws StorageUnavailableException Failed to establish a connection with the Storage Service.
* @throws StorageNotFoundException The storage service was not able to find the dataset for import.
*/
void createTuple(TableDto table, TupleDto data) throws SQLException,
void createTuple(DatabaseDto database, TableDto table, TupleDto data) throws SQLException,
QueryMalformedException, TableMalformedException, StorageUnavailableException, StorageNotFoundException;
/**
......@@ -116,6 +117,6 @@ public interface TableService {
* @throws QueryMalformedException The update query is malformed, likely due to a bug in the application.
* @throws TableMalformedException The tuple is malformed and does not fit the table schema.
*/
void updateTuple(TableDto table, TupleUpdateDto data) throws SQLException,
void updateTuple(DatabaseDto database, TableDto table, TupleUpdateDto data) throws SQLException,
QueryMalformedException, TableMalformedException;
}
package at.tuwien.service;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewDto;
import at.tuwien.exception.QueryMalformedException;
import at.tuwien.exception.ViewMalformedException;
......@@ -12,20 +13,22 @@ public interface ViewService {
/**
* Deletes a view.
*
* @param database The database.
* @param view The view.
* @throws SQLException The connection to the data database was unsuccessful.
* @throws ViewMalformedException The query is malformed and was rejected by the data database.
*/
void delete(ViewDto view) throws SQLException, ViewMalformedException;
void delete(DatabaseDto database, ViewDto view) throws SQLException, ViewMalformedException;
/**
* Counts tuples in a view at system-versioned timestamp.
*
* @param database The database.
* @param view The view.
* @param timestamp The system-versioned timestamp.
* @return The number of tuples.
* @throws SQLException The connection to the data database was unsuccessful.
* @throws QueryMalformedException The query is malformed and was rejected by the data database.
*/
Long count(ViewDto view, Instant timestamp) throws SQLException, QueryMalformedException;
Long count(DatabaseDto database, ViewDto view, Instant timestamp) throws SQLException, QueryMalformedException;
}
......@@ -8,7 +8,7 @@ import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.exception.*;
import at.tuwien.gateway.MetadataServiceGateway;
import at.tuwien.service.CredentialService;
import at.tuwien.service.CacheService;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -18,33 +18,32 @@ import java.util.UUID;
@Log4j2
@Service
public class CredentialServiceImpl implements CredentialService {
public class CacheServiceImpl implements CacheService {
private final MetadataServiceGateway gateway;
private final Cache<UUID, UserDto> userCache;
private final Cache<Long, ViewDto> viewCache;
private final Cache<Long, DatabaseAccessDto> accessCache;
private final Cache<Long, TableDto> tableCache;
private final Cache<Long, DatabaseDto> databaseCache;
private final Cache<Long, ContainerDto> containerCache;
private final Cache<UUID, ViewDto> viewCache;
private final Cache<UUID, TableDto> tableCache;
private final Cache<UUID, DatabaseDto> databaseCache;
private final Cache<UUID, ContainerDto> containerCache;
private final Cache<UUID, DatabaseAccessDto> accessCache;
@Autowired
public CredentialServiceImpl(MetadataServiceGateway gateway, Cache<UUID, UserDto> userCache,
Cache<Long, ViewDto> viewCache, Cache<Long, DatabaseAccessDto> accessCache,
Cache<Long, TableDto> tableCache,
Cache<Long, DatabaseDto> databaseCache,
Cache<Long, ContainerDto> containerCache) {
public CacheServiceImpl(MetadataServiceGateway gateway, Cache<UUID, UserDto> userCache,
Cache<UUID, ViewDto> viewCache, Cache<UUID, TableDto> tableCache,
Cache<UUID, DatabaseAccessDto> accessCache, Cache<UUID, DatabaseDto> databaseCache,
Cache<UUID, ContainerDto> containerCache) {
this.gateway = gateway;
this.userCache = userCache;
this.viewCache = viewCache;
this.accessCache = accessCache;
this.tableCache = tableCache;
this.accessCache = accessCache;
this.databaseCache = databaseCache;
this.containerCache = containerCache;
}
@Override
public DatabaseDto getDatabase(Long id) throws DatabaseNotFoundException, RemoteUnavailableException,
public DatabaseDto getDatabase(UUID id) throws DatabaseNotFoundException, RemoteUnavailableException,
MetadataServiceException {
final DatabaseDto cacheDatabase = databaseCache.getIfPresent(id);
if (cacheDatabase != null) {
......@@ -58,7 +57,7 @@ public class CredentialServiceImpl implements CredentialService {
}
@Override
public TableDto getTable(Long databaseId, Long tableId) throws RemoteUnavailableException,
public TableDto getTable(UUID databaseId, UUID tableId) throws RemoteUnavailableException,
MetadataServiceException, TableNotFoundException {
final TableDto cacheTable = tableCache.getIfPresent(tableId);
if (cacheTable != null) {
......@@ -72,13 +71,7 @@ public class CredentialServiceImpl implements CredentialService {
}
@Override
public void invalidateAccess(Long databaseId) {
accessCache.invalidate(databaseId);
log.debug("invalidated access for database with id {} in cache", databaseId);
}
@Override
public ContainerDto getContainer(Long id) throws RemoteUnavailableException, MetadataServiceException,
public ContainerDto getContainer(UUID id) throws RemoteUnavailableException, MetadataServiceException,
ContainerNotFoundException {
final ContainerDto cacheContainer = containerCache.getIfPresent(id);
if (cacheContainer != null) {
......@@ -92,7 +85,7 @@ public class CredentialServiceImpl implements CredentialService {
}
@Override
public ViewDto getView(Long databaseId, Long viewId) throws RemoteUnavailableException,
public ViewDto getView(UUID databaseId, UUID viewId) throws RemoteUnavailableException,
MetadataServiceException, ViewNotFoundException {
final ViewDto cacheView = viewCache.getIfPresent(viewId);
if (cacheView != null) {
......@@ -120,7 +113,7 @@ public class CredentialServiceImpl implements CredentialService {
}
@Override
public DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException,
public DatabaseAccessDto getAccess(UUID databaseId, UUID userId) throws RemoteUnavailableException,
MetadataServiceException, NotAllowedException {
final DatabaseAccessDto cacheAccess = accessCache.getIfPresent(databaseId);
if (cacheAccess != null) {
......
......@@ -71,10 +71,6 @@ public class ContainerServiceMariaDbImpl extends DataConnector implements Contai
try {
/* create query store */
long start = System.currentTimeMillis();
connection.prepareStatement(mariaDbMapper.queryStoreCreateSequenceRawQuery())
.execute();
log.trace("executed statement in {} ms", System.currentTimeMillis() - start);
start = System.currentTimeMillis();
connection.prepareStatement(mariaDbMapper.queryStoreCreateTableRawQuery())
.execute();
log.trace("executed statement in {} ms", System.currentTimeMillis() - start);
......
......@@ -2,8 +2,6 @@ package at.tuwien.service.impl;
import at.tuwien.api.container.ContainerDto;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewDto;
import at.tuwien.api.database.table.TableDto;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
......@@ -25,14 +23,6 @@ public abstract class DataConnector {
return dataSource;
}
public ComboPooledDataSource getDataSource(ViewDto view) {
return getDataSource(view.getDatabase().getContainer(), view.getDatabase().getInternalName());
}
public ComboPooledDataSource getDataSource(TableDto table) {
return getDataSource(table.getDatabase().getContainer(), table.getDatabase().getInternalName());
}
public ComboPooledDataSource getDataSource(ContainerDto container) {
return getDataSource(container, null);
}
......@@ -48,10 +38,6 @@ public abstract class DataConnector {
return sb.toString();
}
public String getSparkUrl(TableDto table) {
return getSparkUrl(table.getDatabase().getContainer(), table.getDatabase().getInternalName());
}
public String getSparkUrl(DatabaseDto databaseDto) {
return getSparkUrl(databaseDto.getContainer(), databaseDto.getInternalName());
}
......
package at.tuwien.service.impl;
import at.tuwien.api.database.CreateViewDto;
import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewDto;
import at.tuwien.api.database.table.TableDto;
import at.tuwien.api.database.table.constraints.unique.UniqueDto;
import at.tuwien.api.database.table.internal.TableCreateDto;
import at.tuwien.api.user.internal.UpdateUserPasswordDto;
import at.tuwien.config.QueryConfig;
import at.tuwien.exception.*;
import at.tuwien.mapper.DataMapper;
import at.tuwien.mapper.MariaDbMapper;
......@@ -32,15 +30,13 @@ import java.util.List;
public class DatabaseServiceMariaDbImpl extends DataConnector implements DatabaseService {
private final DataMapper dataMapper;
private final QueryConfig queryConfig;
private final MariaDbMapper mariaDbMapper;
private final MetadataMapper metadataMapper;
@Autowired
public DatabaseServiceMariaDbImpl(DataMapper dataMapper, QueryConfig queryConfig, MariaDbMapper mariaDbMapper,
public DatabaseServiceMariaDbImpl(DataMapper dataMapper, MariaDbMapper mariaDbMapper,
MetadataMapper metadataMapper) {
this.dataMapper = dataMapper;
this.queryConfig = queryConfig;
this.mariaDbMapper = mariaDbMapper;
this.metadataMapper = metadataMapper;
}
......@@ -62,7 +58,7 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas
throw new ViewNotFoundException("Failed to find view in the information schema");
}
final ViewDto view = dataMapper.schemaResultSetToView(database, resultSet1);
view.setVdbid(database.getId());
view.setDatabaseId(database.getId());
view.setOwner(database.getOwner());
/* obtain view columns */
start = System.currentTimeMillis();
......@@ -100,7 +96,8 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas
try {
/* create table if not exists */
final long start = System.currentTimeMillis();
connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(data))
connection.prepareStatement(mariaDbMapper.tableCreateDtoToCreateTableRawQuery(database.getInternalName(),
data))
.execute();
log.trace("executed statement in {} ms", System.currentTimeMillis() - start);
connection.commit();
......@@ -115,34 +112,33 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas
} finally {
dataSource.close();
}
log.info("Created table with name {}", tableName);
final TableDto table = inspectTable(database, tableName);
return table;
log.info("Created table with name {}.{}", database.getInternalName(), tableName);
return inspectTable(database, tableName);
}
@Override
public ViewDto createView(DatabaseDto database, CreateViewDto data) throws SQLException,
public ViewDto createView(DatabaseDto database, String viewName, String query) throws SQLException,
ViewMalformedException {
final ComboPooledDataSource dataSource = getDataSource(database);
final Connection connection = dataSource.getConnection();
ViewDto view = ViewDto.builder()
.name(data.getName())
.internalName(mariaDbMapper.nameToInternalName(data.getName()))
.query(data.getQuery())
.name(viewName)
.internalName(mariaDbMapper.nameToInternalName(viewName))
.query(query)
.queryHash(Hashing.sha256()
.hashString(data.getQuery(), StandardCharsets.UTF_8)
.hashString(query, StandardCharsets.UTF_8)
.toString())
.isPublic(database.getIsPublic())
.owner(database.getOwner())
.identifiers(new LinkedList<>())
.isInitialView(false)
.vdbid(database.getId())
.databaseId(database.getId())
.columns(new LinkedList<>())
.build();
try {
/* create view if not exists */
final long start = System.currentTimeMillis();
connection.prepareStatement(mariaDbMapper.viewCreateRawQuery(view.getInternalName(), data.getQuery()))
connection.prepareStatement(mariaDbMapper.viewCreateRawQuery(view.getInternalName(), query))
.execute();
log.trace("executed statement in {} ms", System.currentTimeMillis() - start);
/* select view columns */
......@@ -151,7 +147,7 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas
statement2.setString(2, view.getInternalName());
final ResultSet resultSet2 = statement2.executeQuery();
while (resultSet2.next()) {
view = dataMapper.resultSetToTable(resultSet2, view, queryConfig);
view = dataMapper.resultSetToTable(resultSet2, view);
}
connection.commit();
} catch (SQLException e) {
......@@ -296,7 +292,7 @@ public class DatabaseServiceMariaDbImpl extends DataConnector implements Databas
});
}
}
table.setTdbid(database.getId());
table.setDatabaseId(database.getId());
table.setOwner(database.getOwner());
final TableDto tmpTable = table;
tmpTable.getColumns()
......