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 892 additions and 492 deletions
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite name="pytest" errors="0" failures="0" skipped="0" tests="48" time="25.604" timestamp="2025-01-29T15:46:14.797988+01:00" hostname="medusa"><testcase classname="test.test_app.JwtTest" name="test_delete_database_no_auth_fails" time="11.131" /><testcase classname="test.test_app.JwtTest" name="test_delete_database_no_role_fails" time="0.317" /><testcase classname="test.test_app.JwtTest" name="test_delete_database_not_found_fails" time="0.370" /><testcase classname="test.test_app.JwtTest" name="test_delete_database_succeeds" time="0.524" /><testcase classname="test.test_app.JwtTest" name="test_get_fields_fails" time="0.154" /><testcase classname="test.test_app.JwtTest" name="test_get_fields_succeeds" time="0.218" /><testcase classname="test.test_app.JwtTest" name="test_get_fuzzy_search_no_query_fails" time="0.173" /><testcase classname="test.test_app.JwtTest" name="test_get_fuzzy_search_succeeds" time="0.283" /><testcase classname="test.test_app.JwtTest" name="test_get_index_fails" time="0.240" /><testcase classname="test.test_app.JwtTest" name="test_get_index_succeeds" time="0.190" /><testcase classname="test.test_app.JwtTest" name="test_health_succeeds" time="0.160" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_column_succeeds" time="0.386" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_concept_succeeds" time="0.341" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_identifier_succeeds" time="0.312" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_media_type_fails" time="0.140" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_no_body_fails" time="0.134" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_succeeds" time="0.284" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_table_succeeds" time="0.336" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_unit_succeeds" time="0.246" /><testcase classname="test.test_app.JwtTest" name="test_post_general_search_view_succeeds" time="0.281" /><testcase classname="test.test_app.JwtTest" name="test_update_database_empty_body_fails" time="0.177" /><testcase classname="test.test_app.JwtTest" name="test_update_database_malformed_body_fails" time="0.180" /><testcase classname="test.test_app.JwtTest" name="test_update_database_media_type_fails" time="0.231" /><testcase classname="test.test_app.JwtTest" name="test_update_database_no_auth_fails" time="0.119" /><testcase classname="test.test_app.JwtTest" name="test_update_database_no_body_fails" time="0.150" /><testcase classname="test.test_app.JwtTest" name="test_update_database_succeeds" time="0.243" /><testcase classname="test.test_jwt.JwtTest" name="test_get_user_roles_succeeds" time="0.146" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_password_empty_password_fails" time="0.144" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_password_empty_username_fails" time="0.127" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_password_no_password_fails" time="0.142" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_password_no_username_fails" time="0.146" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_password_succeeds" time="0.152" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_token_empty_token_fails" time="0.144" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_token_malformed_token_fails" time="0.143" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_token_no_token_fails" time="0.130" /><testcase classname="test.test_jwt.JwtTest" name="test_verify_token_succeeds" time="0.212" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_delete_database_fails" time="0.120" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_delete_database_succeeds" time="0.172" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_fuzzy_search_succeeds" time="0.190" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_get_fields_for_index_database_succeeds" time="0.201" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_get_fields_for_index_user_succeeds" time="0.202" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_unit_independent_search_fails" time="0.208" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_update_database_create_succeeds" time="0.205" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_update_database_malformed_fails" time="0.237" /><testcase classname="test.test_opensearch_client.OpenSearchClientTest" name="test_update_database_succeeds" time="0.214" /><testcase classname="test.test_keycloak_client.JwtTest" name="test_obtain_user_token_malformed_fails" time="0.112" /><testcase classname="test.test_keycloak_client.JwtTest" name="test_obtain_user_token_succeeds" time="0.149" /><testcase classname="test.test_keycloak_client.JwtTest" name="test_verify_jwt_succeeds" time="0.684" /></testsuite></testsuites>
\ No newline at end of file
...@@ -8,10 +8,15 @@ ...@@ -8,10 +8,15 @@
<version>3.3.5</version> <version>3.3.5</version>
</parent> </parent>
<organization>
<name>TU Wien</name>
<url>https://www.tuwien.ac.at</url>
</organization>
<groupId>at.tuwien</groupId> <groupId>at.tuwien</groupId>
<artifactId>dbrepo-data-service</artifactId> <artifactId>dbrepo-data-service</artifactId>
<name>dbrepo-data-service</name> <name>dbrepo-data-service</name>
<version>1.6.5</version> <version>1.7.0</version>
<description>Service that manages the data</description> <description>Service that manages the data</description>
...@@ -23,7 +28,7 @@ ...@@ -23,7 +28,7 @@
<module>report</module> <module>report</module>
</modules> </modules>
<url>https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.6/</url> <url>https://www.ifs.tuwien.ac.at/infrastructures/dbrepo/1.7/</url>
<developers> <developers>
<developer> <developer>
<name>Martin Weise</name> <name>Martin Weise</name>
...@@ -54,14 +59,37 @@ ...@@ -54,14 +59,37 @@
<minio.version>8.5.7</minio.version> <minio.version>8.5.7</minio.version>
<guava.version>33.3.0-jre</guava.version> <guava.version>33.3.0-jre</guava.version>
<spark.version>4.0.0-preview2</spark.version> <spark.version>4.0.0-preview2</spark.version>
<keycloak.version>26.0.4</keycloak.version>
<scala.version>2.13</scala.version> <scala.version>2.13</scala.version>
<antlr-runtime.version>3.5.2</antlr-runtime.version> <antlr-runtime.version>3.5.2</antlr-runtime.version>
<micrometer.version>1.10.0</micrometer.version> <micrometer.version>1.10.0</micrometer.version>
<!-- see https://github.com/apache/spark/blob/cde8e4a82e20a363861f451ebd5138efb3194ab8/pom.xml --> <!-- see https://github.com/apache/spark/blob/cde8e4a82e20a363861f451ebd5138efb3194ab8/pom.xml -->
<hadoop.version>3.4.0</hadoop.version> <hadoop.version>3.4.0</hadoop.version>
<jakarta-servlet.version>5.0.0</jakarta-servlet.version> <jakarta-servlet.version>5.0.0</jakarta-servlet.version>
<sonar.coverage.jacoco.xmlReportPaths>./report/target/site/jacoco-aggregate/jacoco.xml <sonar.coverage.jacoco.xmlReportPaths>
./report/target/site/jacoco-aggregate/jacoco.xml
</sonar.coverage.jacoco.xmlReportPaths> </sonar.coverage.jacoco.xmlReportPaths>
<CodeCacheSize>128m</CodeCacheSize>
<extraJavaTestArgs>
-XX:+IgnoreUnrecognizedVMOptions
--add-modules=jdk.incubator.vector
--add-opens=java.base/java.lang=ALL-UNNAMED
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
--add-opens=java.base/java.io=ALL-UNNAMED
--add-opens=java.base/java.net=ALL-UNNAMED
--add-opens=java.base/java.nio=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED
--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED
--add-opens=java.base/sun.nio.cs=ALL-UNNAMED
--add-opens=java.base/sun.security.action=ALL-UNNAMED
--add-opens=java.base/sun.util.calendar=ALL-UNNAMED
-Djdk.reflect.useDirectMethodHandle=false
-Dio.netty.tryReflectionSetAccessible=true
</extraJavaTestArgs>
</properties> </properties>
<dependencies> <dependencies>
...@@ -208,6 +236,22 @@ ...@@ -208,6 +236,22 @@
<artifactId>commons-validator</artifactId> <artifactId>commons-validator</artifactId>
<version>${commons-validator.version}</version> <version>${commons-validator.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>${jooq.version}</version>
</dependency>
<!-- Authentication -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-common</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate6</artifactId> <artifactId>jackson-datatype-hibernate6</artifactId>
...@@ -323,7 +367,26 @@ ...@@ -323,7 +367,26 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<!-- Surefire runs all Java tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<!-- Note config is repeated in scalatest config -->
<configuration>
<argLine>@{argLine} -ea -Xmx4g -Xss4m -XX:MaxMetaspaceSize=2g -XX:ReservedCodeCacheSize=${CodeCacheSize}
${extraJavaTestArgs}
</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
<licenses>
<license>
<name>Apache-2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
</project> </project>
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
<parent> <parent>
<groupId>at.tuwien</groupId> <groupId>at.tuwien</groupId>
<artifactId>dbrepo-data-service</artifactId> <artifactId>dbrepo-data-service</artifactId>
<version>1.6.5</version> <version>1.7.0</version>
</parent> </parent>
<artifactId>dbrepo-data-service-querystore</artifactId> <artifactId>dbrepo-data-service-querystore</artifactId>
<name>dbrepo-data-service-querystore</name> <name>dbrepo-data-service-querystore</name>
<version>1.6.5</version> <version>1.7.0</version>
<dependencies/> <dependencies/>
......
...@@ -6,12 +6,12 @@ ...@@ -6,12 +6,12 @@
<parent> <parent>
<groupId>at.tuwien</groupId> <groupId>at.tuwien</groupId>
<artifactId>dbrepo-data-service</artifactId> <artifactId>dbrepo-data-service</artifactId>
<version>1.6.5</version> <version>1.7.0</version>
</parent> </parent>
<artifactId>report</artifactId> <artifactId>report</artifactId>
<name>dbrepo-data-service-report</name> <name>dbrepo-data-service-report</name>
<version>1.6.5</version> <version>1.7.0</version>
<description> <description>
This module is only intended for the pipeline coverage report. See the detailed report in the This module is only intended for the pipeline coverage report. See the detailed report in the
respective modules respective modules
......
...@@ -6,18 +6,18 @@ ...@@ -6,18 +6,18 @@
<parent> <parent>
<groupId>at.tuwien</groupId> <groupId>at.tuwien</groupId>
<artifactId>dbrepo-data-service</artifactId> <artifactId>dbrepo-data-service</artifactId>
<version>1.6.5</version> <version>1.7.0</version>
</parent> </parent>
<artifactId>rest-service</artifactId> <artifactId>rest-service</artifactId>
<name>dbrepo-data-service-rest-service</name> <name>dbrepo-data-service-rest-service</name>
<version>1.6.5</version> <version>1.7.0</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>at.tuwien</groupId> <groupId>at.tuwien</groupId>
<artifactId>services</artifactId> <artifactId>services</artifactId>
<version>1.6.5</version> <version>1.7.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
......
...@@ -6,7 +6,7 @@ import at.tuwien.api.error.ApiErrorDto; ...@@ -6,7 +6,7 @@ import at.tuwien.api.error.ApiErrorDto;
import at.tuwien.api.user.UserDto; import at.tuwien.api.user.UserDto;
import at.tuwien.exception.*; import at.tuwien.exception.*;
import at.tuwien.service.AccessService; import at.tuwien.service.AccessService;
import at.tuwien.service.CredentialService; import at.tuwien.service.CacheService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
...@@ -31,13 +31,13 @@ import java.util.UUID; ...@@ -31,13 +31,13 @@ import java.util.UUID;
@RequestMapping(path = "/api/database/{databaseId}/access") @RequestMapping(path = "/api/database/{databaseId}/access")
public class AccessEndpoint extends RestEndpoint { public class AccessEndpoint extends RestEndpoint {
private final CacheService cacheService;
private final AccessService accessService; private final AccessService accessService;
private final CredentialService credentialService;
@Autowired @Autowired
public AccessEndpoint(AccessService accessService, CredentialService credentialService) { public AccessEndpoint(CacheService cacheService, AccessService accessService) {
this.cacheService = cacheService;
this.accessService = accessService; this.accessService = accessService;
this.credentialService = credentialService;
} }
@PostMapping("/{userId}") @PostMapping("/{userId}")
...@@ -74,14 +74,14 @@ public class AccessEndpoint extends RestEndpoint { ...@@ -74,14 +74,14 @@ public class AccessEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<Void> create(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<Void> create(@NotNull @PathVariable("databaseId") UUID databaseId,
@PathVariable("userId") UUID userId, @PathVariable("userId") UUID userId,
@Valid @RequestBody CreateAccessDto data) @Valid @RequestBody CreateAccessDto data)
throws NotAllowedException, DatabaseUnavailableException, DatabaseNotFoundException, throws NotAllowedException, DatabaseUnavailableException, DatabaseNotFoundException,
RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException { RemoteUnavailableException, UserNotFoundException, DatabaseMalformedException, MetadataServiceException {
log.debug("endpoint give access to database, databaseId={}, userId={}", databaseId, userId); log.debug("endpoint give access to database, databaseId={}, userId={}", databaseId, userId);
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
final UserDto user = credentialService.getUser(userId); final UserDto user = cacheService.getUser(userId);
if (database.getAccesses().stream().anyMatch(a -> a.getUser().getId().equals(userId))) { if (database.getAccesses().stream().anyMatch(a -> a.getUser().getId().equals(userId))) {
log.error("Failed to create access to user with id {}: already has access", userId); log.error("Failed to create access to user with id {}: already has access", userId);
throw new NotAllowedException("Failed to create access to user with id " + userId + ": already has access"); throw new NotAllowedException("Failed to create access to user with id " + userId + ": already has access");
...@@ -130,15 +130,15 @@ public class AccessEndpoint extends RestEndpoint { ...@@ -130,15 +130,15 @@ public class AccessEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") UUID databaseId,
@PathVariable("userId") UUID userId, @PathVariable("userId") UUID userId,
@Valid @RequestBody CreateAccessDto access) throws NotAllowedException, @Valid @RequestBody CreateAccessDto access) throws NotAllowedException,
DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException,
DatabaseMalformedException, MetadataServiceException { DatabaseMalformedException, MetadataServiceException {
log.debug("endpoint modify access to database, databaseId={}, userId={}, access.type={}", databaseId, userId, log.debug("endpoint modify access to database, databaseId={}, userId={}, access.type={}", databaseId, userId,
access.getType()); access.getType());
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
final UserDto user = credentialService.getUser(userId); final UserDto user = cacheService.getUser(userId);
if (database.getAccesses().stream().noneMatch(a -> a.getHuserid().equals(userId))) { if (database.getAccesses().stream().noneMatch(a -> a.getHuserid().equals(userId))) {
log.error("Failed to update access to user with id {}: no access", userId); log.error("Failed to update access to user with id {}: no access", userId);
throw new NotAllowedException("Failed to update access to user with id " + userId + ": no access"); throw new NotAllowedException("Failed to update access to user with id " + userId + ": no access");
...@@ -153,22 +153,6 @@ public class AccessEndpoint extends RestEndpoint { ...@@ -153,22 +153,6 @@ public class AccessEndpoint extends RestEndpoint {
} }
} }
@PutMapping
@PreAuthorize("hasAuthority('system')")
@Operation(summary = "Invalidate access cache for database",
security = {@SecurityRequirement(name = "basicAuth")},
hidden = true)
@ApiResponses(value = {
@ApiResponse(responseCode = "202",
description = "Invalidated access cache succeeded")
})
public ResponseEntity<Void> invalidateAccess(@NotNull @PathVariable("databaseId") Long databaseId) {
log.debug("endpoint empty access cache for database, databaseId={}", databaseId);
credentialService.invalidateAccess(databaseId);
return ResponseEntity.accepted()
.build();
}
@DeleteMapping("/{userId}") @DeleteMapping("/{userId}")
@PreAuthorize("hasAuthority('system')") @PreAuthorize("hasAuthority('system')")
@Operation(summary = "Revoke access", @Operation(summary = "Revoke access",
...@@ -203,13 +187,13 @@ public class AccessEndpoint extends RestEndpoint { ...@@ -203,13 +187,13 @@ public class AccessEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<Void> revoke(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<Void> revoke(@NotNull @PathVariable("databaseId") UUID databaseId,
@PathVariable("userId") UUID userId) throws NotAllowedException, @PathVariable("userId") UUID userId) throws NotAllowedException,
DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException,
DatabaseMalformedException, MetadataServiceException { DatabaseMalformedException, MetadataServiceException {
log.debug("endpoint revoke access to database, databaseId={}, userId={}", databaseId, userId); log.debug("endpoint revoke access to database, databaseId={}, userId={}", databaseId, userId);
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
final UserDto user = credentialService.getUser(userId); final UserDto user = cacheService.getUser(userId);
if (database.getAccesses().stream().noneMatch(a -> a.getUser().getId().equals(userId))) { if (database.getAccesses().stream().noneMatch(a -> a.getUser().getId().equals(userId))) {
log.error("Failed to delete access to user with id {}: no access", userId); log.error("Failed to delete access to user with id {}: no access", userId);
throw new NotAllowedException("Failed to delete access to user with id " + userId + ": no access"); throw new NotAllowedException("Failed to delete access to user with id " + userId + ": no access");
......
...@@ -10,7 +10,7 @@ import at.tuwien.api.user.internal.UpdateUserPasswordDto; ...@@ -10,7 +10,7 @@ import at.tuwien.api.user.internal.UpdateUserPasswordDto;
import at.tuwien.exception.*; import at.tuwien.exception.*;
import at.tuwien.service.AccessService; import at.tuwien.service.AccessService;
import at.tuwien.service.ContainerService; import at.tuwien.service.ContainerService;
import at.tuwien.service.CredentialService; import at.tuwien.service.CacheService;
import at.tuwien.service.DatabaseService; import at.tuwien.service.DatabaseService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
...@@ -28,6 +28,7 @@ import org.springframework.security.access.prepost.PreAuthorize; ...@@ -28,6 +28,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.UUID;
@Log4j2 @Log4j2
@RestController @RestController
...@@ -35,18 +36,18 @@ import java.sql.SQLException; ...@@ -35,18 +36,18 @@ import java.sql.SQLException;
@RequestMapping(path = "/api/database") @RequestMapping(path = "/api/database")
public class DatabaseEndpoint extends RestEndpoint { public class DatabaseEndpoint extends RestEndpoint {
private final CacheService cacheService;
private final AccessService accessService; private final AccessService accessService;
private final DatabaseService databaseService; private final DatabaseService databaseService;
private final ContainerService containerService; private final ContainerService containerService;
private final CredentialService credentialService;
@Autowired @Autowired
public DatabaseEndpoint(AccessService accessService, DatabaseService databaseService, public DatabaseEndpoint(CacheService cacheService, AccessService accessService, DatabaseService databaseService,
ContainerService containerService, CredentialService credentialService) { ContainerService containerService) {
this.cacheService = cacheService;
this.accessService = accessService; this.accessService = accessService;
this.databaseService = databaseService; this.databaseService = databaseService;
this.containerService = containerService; this.containerService = containerService;
this.credentialService = credentialService;
} }
@PostMapping @PostMapping
...@@ -86,7 +87,7 @@ public class DatabaseEndpoint extends RestEndpoint { ...@@ -86,7 +87,7 @@ public class DatabaseEndpoint extends RestEndpoint {
DatabaseMalformedException, QueryStoreCreateException, MetadataServiceException { DatabaseMalformedException, QueryStoreCreateException, MetadataServiceException {
log.debug("endpoint create database, data.containerId={}, data.internalName={}, data.username={}", log.debug("endpoint create database, data.containerId={}, data.internalName={}, data.username={}",
data.getContainerId(), data.getInternalName(), data.getUsername()); data.getContainerId(), data.getInternalName(), data.getUsername());
final ContainerDto container = credentialService.getContainer(data.getContainerId()); final ContainerDto container = cacheService.getContainer(data.getContainerId());
try { try {
final DatabaseDto database = containerService.createDatabase(container, data); final DatabaseDto database = containerService.createDatabase(container, data);
containerService.createQueryStore(container, data.getInternalName()); containerService.createQueryStore(container, data.getInternalName());
...@@ -128,13 +129,13 @@ public class DatabaseEndpoint extends RestEndpoint { ...@@ -128,13 +129,13 @@ public class DatabaseEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<Void> update(@NotNull @PathVariable("databaseId") UUID databaseId,
@Valid @RequestBody UpdateUserPasswordDto data) @Valid @RequestBody UpdateUserPasswordDto data)
throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
DatabaseMalformedException, MetadataServiceException { DatabaseMalformedException, MetadataServiceException {
log.debug("endpoint update user password in database, databaseId={}, data.username={}", databaseId, log.debug("endpoint update user password in database, databaseId={}, data.username={}", databaseId,
data.getUsername()); data.getUsername());
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
try { try {
databaseService.update(database, data); databaseService.update(database, data);
return ResponseEntity.status(HttpStatus.ACCEPTED) return ResponseEntity.status(HttpStatus.ACCEPTED)
......
package at.tuwien.endpoints; package at.tuwien.endpoints;
import at.tuwien.ExportResourceDto; import at.tuwien.ExportResourceDto;
import at.tuwien.api.database.CreateViewDto;
import at.tuwien.api.database.DatabaseDto; import at.tuwien.api.database.DatabaseDto;
import at.tuwien.api.database.ViewColumnDto; import at.tuwien.api.database.ViewColumnDto;
import at.tuwien.api.database.ViewDto; import at.tuwien.api.database.ViewDto;
import at.tuwien.api.database.query.ExecuteStatementDto;
import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryDto;
import at.tuwien.api.database.query.QueryPersistDto; import at.tuwien.api.database.query.QueryPersistDto;
import at.tuwien.api.database.query.SubsetDto;
import at.tuwien.api.error.ApiErrorDto; import at.tuwien.api.error.ApiErrorDto;
import at.tuwien.exception.*; import at.tuwien.exception.*;
import at.tuwien.gateway.MetadataServiceGateway;
import at.tuwien.mapper.MariaDbMapper; import at.tuwien.mapper.MariaDbMapper;
import at.tuwien.mapper.MetadataMapper; import at.tuwien.mapper.MetadataMapper;
import at.tuwien.service.*; import at.tuwien.service.CacheService;
import at.tuwien.service.DatabaseService;
import at.tuwien.service.StorageService;
import at.tuwien.service.SubsetService;
import at.tuwien.validation.EndpointValidator; import at.tuwien.validation.EndpointValidator;
import io.micrometer.observation.annotation.Observed; import io.micrometer.observation.annotation.Observed;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
...@@ -50,27 +53,27 @@ import java.util.UUID; ...@@ -50,27 +53,27 @@ import java.util.UUID;
@RequestMapping(path = "/api/database/{databaseId}/subset") @RequestMapping(path = "/api/database/{databaseId}/subset")
public class SubsetEndpoint extends RestEndpoint { public class SubsetEndpoint extends RestEndpoint {
private final CacheService cacheService;
private final MariaDbMapper mariaDbMapper; private final MariaDbMapper mariaDbMapper;
private final SubsetService subsetService; private final SubsetService subsetService;
private final MetadataMapper metadataMapper; private final MetadataMapper metadataMapper;
private final MetricsService metricsService;
private final StorageService storageService; private final StorageService storageService;
private final DatabaseService databaseService; private final DatabaseService databaseService;
private final CredentialService credentialService;
private final EndpointValidator endpointValidator; private final EndpointValidator endpointValidator;
private final MetadataServiceGateway metadataServiceGateway;
@Autowired @Autowired
public SubsetEndpoint(MariaDbMapper mariaDbMapper, SubsetService subsetService, MetadataMapper metadataMapper, public SubsetEndpoint(CacheService cacheService, MariaDbMapper mariaDbMapper, SubsetService subsetService,
MetricsService metricsService, StorageService storageService, DatabaseService databaseService, MetadataMapper metadataMapper, StorageService storageService, DatabaseService databaseService,
CredentialService credentialService, EndpointValidator endpointValidator) { EndpointValidator endpointValidator, MetadataServiceGateway metadataServiceGateway) {
this.cacheService = cacheService;
this.mariaDbMapper = mariaDbMapper; this.mariaDbMapper = mariaDbMapper;
this.subsetService = subsetService; this.subsetService = subsetService;
this.metadataMapper = metadataMapper; this.metadataMapper = metadataMapper;
this.metricsService = metricsService;
this.storageService = storageService; this.storageService = storageService;
this.databaseService = databaseService; this.databaseService = databaseService;
this.credentialService = credentialService;
this.endpointValidator = endpointValidator; this.endpointValidator = endpointValidator;
this.metadataServiceGateway = metadataServiceGateway;
} }
@GetMapping @GetMapping
...@@ -100,13 +103,13 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -100,13 +103,13 @@ public class SubsetEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<List<QueryDto>> list(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<List<QueryDto>> list(@NotNull @PathVariable("databaseId") UUID databaseId,
@RequestParam(name = "persisted", required = false) Boolean filterPersisted, @RequestParam(name = "persisted", required = false) Boolean filterPersisted,
Principal principal) Principal principal)
throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException,
QueryNotFoundException, NotAllowedException, MetadataServiceException { QueryNotFoundException, NotAllowedException, MetadataServiceException {
log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted); log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted);
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
final List<QueryDto> queries; final List<QueryDto> queries;
try { try {
...@@ -157,8 +160,8 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -157,8 +160,8 @@ public class SubsetEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") UUID databaseId,
@NotNull @PathVariable("subsetId") Long subsetId, @NotNull @PathVariable("subsetId") UUID subsetId,
@NotNull @RequestHeader("Accept") String accept, @NotNull @RequestHeader("Accept") String accept,
@RequestParam(required = false) Instant timestamp, @RequestParam(required = false) Instant timestamp,
Principal principal) Principal principal)
...@@ -167,7 +170,7 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -167,7 +170,7 @@ public class SubsetEndpoint extends RestEndpoint {
MetadataServiceException, TableNotFoundException, QueryMalformedException, NotAllowedException { MetadataServiceException, TableNotFoundException, QueryMalformedException, NotAllowedException {
log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId, log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId,
subsetId, accept, timestamp); subsetId, accept, timestamp);
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
final QueryDto subset; final QueryDto subset;
try { try {
...@@ -181,7 +184,7 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -181,7 +184,7 @@ public class SubsetEndpoint extends RestEndpoint {
timestamp = Instant.now(); timestamp = Instant.now();
log.debug("timestamp not set: default to {}", timestamp); log.debug("timestamp not set: default to {}", timestamp);
} }
if (accept == null || accept.isEmpty() || accept.isBlank()) { if (accept == null || accept.isBlank()) {
accept = MediaType.APPLICATION_JSON_VALUE; accept = MediaType.APPLICATION_JSON_VALUE;
log.debug("accept header not set: default to {}", accept); log.debug("accept header not set: default to {}", accept);
} }
...@@ -192,8 +195,7 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -192,8 +195,7 @@ public class SubsetEndpoint extends RestEndpoint {
case "text/csv": case "text/csv":
log.trace("accept header matches csv"); log.trace("accept header matches csv");
final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, null, null); final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, null, null);
final Dataset<Row> dataset = subsetService.getData(database, query, timestamp, null, null, null, null); final Dataset<Row> dataset = subsetService.getData(database, query);
metricsService.countSubsetGetData(databaseId, subsetId);
final ExportResourceDto resource = storageService.transformDataset(dataset); final ExportResourceDto resource = storageService.transformDataset(dataset);
final HttpHeaders headers = new HttpHeaders(); final HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\""); headers.add("Content-Disposition", "attachment; filename=\"" + resource.getFilename() + "\"");
...@@ -247,8 +249,8 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -247,8 +249,8 @@ public class SubsetEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<List<Map<String, Object>>> create(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<List<Map<String, Object>>> create(@NotNull @PathVariable("databaseId") UUID databaseId,
@Valid @RequestBody ExecuteStatementDto data, @Valid @RequestBody SubsetDto data,
Principal principal, Principal principal,
@NotNull HttpServletRequest request, @NotNull HttpServletRequest request,
@RequestParam(required = false) Instant timestamp, @RequestParam(required = false) Instant timestamp,
...@@ -258,13 +260,12 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -258,13 +260,12 @@ public class SubsetEndpoint extends RestEndpoint {
QueryNotFoundException, StorageUnavailableException, QueryMalformedException, StorageNotFoundException, QueryNotFoundException, StorageUnavailableException, QueryMalformedException, StorageNotFoundException,
QueryStoreInsertException, TableMalformedException, PaginationException, QueryNotSupportedException, QueryStoreInsertException, TableMalformedException, PaginationException, QueryNotSupportedException,
NotAllowedException, UserNotFoundException, MetadataServiceException, TableNotFoundException, NotAllowedException, UserNotFoundException, MetadataServiceException, TableNotFoundException,
ViewMalformedException, ViewNotFoundException { ViewMalformedException, ViewNotFoundException, ImageNotFoundException {
log.debug("endpoint create subset in database, databaseId={}, data.statement={}, page={}, size={}, " + log.debug("endpoint create subset in database, databaseId={}, page={}, size={}, timestamp={}", databaseId,
"timestamp={}", databaseId, data.getStatement(), page, size, page, size, timestamp);
timestamp);
/* check */ /* check */
endpointValidator.validateDataParams(page, size); endpointValidator.validateDataParams(page, size);
endpointValidator.validateForbiddenStatements(data.getStatement()); endpointValidator.validateSubsetParams(data);
/* parameters */ /* parameters */
final UUID userId; final UUID userId;
if (principal != null) { if (principal != null) {
...@@ -285,10 +286,10 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -285,10 +286,10 @@ public class SubsetEndpoint extends RestEndpoint {
log.debug("timestamp not set: default to {}", timestamp); log.debug("timestamp not set: default to {}", timestamp);
} }
/* create */ /* create */
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); endpointValidator.validateOnlyPrivateSchemaAccess(database, principal);
try { try {
final Long subsetId = subsetService.create(database, data.getStatement(), timestamp, userId); final UUID subsetId = subsetService.create(database, data, timestamp, userId);
return getData(databaseId, subsetId, principal, request, timestamp, page, size); return getData(databaseId, subsetId, principal, request, timestamp, page, size);
} catch (SQLException e) { } catch (SQLException e) {
log.error("Failed to establish connection to database: {}", e.getMessage()); log.error("Failed to establish connection to database: {}", e.getMessage());
...@@ -304,9 +305,9 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -304,9 +305,9 @@ public class SubsetEndpoint extends RestEndpoint {
@ApiResponses(value = { @ApiResponses(value = {
@ApiResponse(responseCode = "200", @ApiResponse(responseCode = "200",
description = "Retrieved subset data", description = "Retrieved subset data",
headers = {@Header(name = "X-Count", description = "Number of rows", schema = @Schema(implementation = Long.class)), headers = {@Header(name = "X-Count", description = "Number of rows", schema = @Schema(implementation = UUID.class)),
@Header(name = "X-Headers", description = "The list of headers separated by comma", schema = @Schema(implementation = String.class)), @Header(name = "X-Headers", description = "The list of headers separated by comma", schema = @Schema(implementation = String.class)),
@Header(name = "X-Id", description = "The subset id", schema = @Schema(implementation = Long.class), required = true), @Header(name = "X-Id", description = "The subset id", schema = @Schema(implementation = UUID.class), required = true),
@Header(name = "Access-Control-Expose-Headers", description = "Reverse proxy exposing of custom headers", schema = @Schema(implementation = String.class), required = true)}, @Header(name = "Access-Control-Expose-Headers", description = "Reverse proxy exposing of custom headers", schema = @Schema(implementation = String.class), required = true)},
content = {@Content( content = {@Content(
mediaType = "application/json", mediaType = "application/json",
...@@ -332,26 +333,26 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -332,26 +333,26 @@ public class SubsetEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<List<Map<String, Object>>> getData(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<List<Map<String, Object>>> getData(@NotNull @PathVariable("databaseId") UUID databaseId,
@NotNull @PathVariable("subsetId") Long subsetId, @NotNull @PathVariable("subsetId") UUID subsetId,
Principal principal, Principal principal,
@NotNull HttpServletRequest request, @NotNull HttpServletRequest request,
@RequestParam(required = false) Instant timestamp, @RequestParam(required = false) Instant timestamp,
@RequestParam(required = false) Long page, @RequestParam(required = false) Long page,
@RequestParam(required = false) Long size) @RequestParam(required = false) Long size)
throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException, throws PaginationException, DatabaseNotFoundException, RemoteUnavailableException, NotAllowedException,
QueryNotFoundException, DatabaseUnavailableException, TableMalformedException, QueryMalformedException, QueryNotFoundException, DatabaseUnavailableException, QueryMalformedException, UserNotFoundException,
UserNotFoundException, MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException { MetadataServiceException, TableNotFoundException, ViewNotFoundException, ViewMalformedException {
log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}", log.debug("endpoint get subset data, databaseId={}, subsetId={}, principal.name={} page={}, size={}",
databaseId, subsetId, principal != null ? principal.getName() : null, page, size); databaseId, subsetId, principal != null ? principal.getName() : null, page, size);
endpointValidator.validateDataParams(page, size); endpointValidator.validateDataParams(page, size);
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
if (!database.getIsPublic()) { if (!database.getIsPublic()) {
if (principal == null) { if (principal == null) {
log.error("Failed to re-execute query: no authentication found"); log.error("Failed to re-execute query: no authentication found");
throw new NotAllowedException("Failed to re-execute query: no authentication found"); throw new NotAllowedException("Failed to re-execute query: no authentication found");
} }
credentialService.getAccess(databaseId, getId(principal)); cacheService.getAccess(databaseId, getId(principal));
} }
log.trace("visibility for database: is_public={}, is_schema_public={}", database.getIsPublic(), database.getIsSchemaPublic()); log.trace("visibility for database: is_public={}, is_schema_public={}", database.getIsPublic(), database.getIsSchemaPublic());
/* parameters */ /* parameters */
...@@ -379,16 +380,11 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -379,16 +380,11 @@ public class SubsetEndpoint extends RestEndpoint {
.headers(headers) .headers(headers)
.build(); .build();
} }
subset.setIdentifiers(metadataServiceGateway.getIdentifiers(database.getId(), subset.getId()));
final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, page, size); final String query = mariaDbMapper.rawSelectQuery(subset.getQuery(), timestamp, page, size);
final Dataset<Row> dataset = subsetService.getData(database, query, timestamp, page, size, null, null); final Dataset<Row> dataset = subsetService.getData(database, query);
metricsService.countSubsetGetData(databaseId, subsetId);
final String viewName = metadataMapper.queryDtoToViewName(subset); final String viewName = metadataMapper.queryDtoToViewName(subset);
databaseService.createView(database, CreateViewDto.builder() databaseService.createView(database, viewName, subset.getQuery());
.name(viewName)
.isPublic(false)
.isSchemaPublic(false)
.query(query)
.build());
final ViewDto view = databaseService.inspectView(database, viewName); final ViewDto view = databaseService.inspectView(database, viewName);
headers.set("Access-Control-Expose-Headers", "X-Id X-Headers"); headers.set("Access-Control-Expose-Headers", "X-Id X-Headers");
headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList())); headers.set("X-Headers", String.join(",", view.getColumns().stream().map(ViewColumnDto::getInternalName).toList()));
...@@ -439,16 +435,16 @@ public class SubsetEndpoint extends RestEndpoint { ...@@ -439,16 +435,16 @@ public class SubsetEndpoint extends RestEndpoint {
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = ApiErrorDto.class))}), schema = @Schema(implementation = ApiErrorDto.class))}),
}) })
public ResponseEntity<QueryDto> persist(@NotNull @PathVariable("databaseId") Long databaseId, public ResponseEntity<QueryDto> persist(@NotNull @PathVariable("databaseId") UUID databaseId,
@NotNull @PathVariable("queryId") Long queryId, @NotNull @PathVariable("queryId") UUID queryId,
@NotNull @Valid @RequestBody QueryPersistDto data, @NotNull @Valid @RequestBody QueryPersistDto data,
@NotNull Principal principal) throws NotAllowedException, @NotNull Principal principal) throws NotAllowedException,
RemoteUnavailableException, DatabaseNotFoundException, QueryStorePersistException, RemoteUnavailableException, DatabaseNotFoundException, QueryStorePersistException,
DatabaseUnavailableException, QueryNotFoundException, UserNotFoundException, MetadataServiceException { DatabaseUnavailableException, QueryNotFoundException, UserNotFoundException, MetadataServiceException {
log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId, log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId,
queryId, data.getPersist(), principal.getName()); queryId, data.getPersist(), principal.getName());
final DatabaseDto database = credentialService.getDatabase(databaseId); final DatabaseDto database = cacheService.getDatabase(databaseId);
credentialService.getAccess(databaseId, getId(principal)); cacheService.getAccess(databaseId, getId(principal));
try { try {
subsetService.persist(database, queryId, data.getPersist()); subsetService.persist(database, queryId, data.getPersist());
final QueryDto dto = subsetService.findById(database, queryId); final QueryDto dto = subsetService.findById(database, queryId);
......
...@@ -458,6 +458,13 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler { ...@@ -458,6 +458,13 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
return generic_handle(e.getClass(), e.getLocalizedMessage()); return generic_handle(e.getClass(), e.getLocalizedMessage());
} }
@Hidden
@ResponseStatus(code = HttpStatus.CONFLICT)
@ExceptionHandler(ViewExistsException.class)
public ResponseEntity<ApiErrorDto> handle(ViewExistsException e) {
return generic_handle(e.getClass(), e.getLocalizedMessage());
}
@Hidden @Hidden
@ResponseStatus(code = HttpStatus.BAD_REQUEST) @ResponseStatus(code = HttpStatus.BAD_REQUEST)
@ExceptionHandler(ViewMalformedException.class) @ExceptionHandler(ViewMalformedException.class)
......
CREATE PROCEDURE hash_table(IN name VARCHAR(255), OUT hash VARCHAR(255), OUT count BIGINT) BEGIN DECLARE _sql TEXT; SELECT CONCAT('SELECT SHA2(GROUP_CONCAT(CONCAT_WS(\'\',', GROUP_CONCAT(CONCAT('`', column_name, '`') ORDER BY column_name), ') SEPARATOR \',\'), 256) AS hash, COUNT(*) AS count FROM `', name, '` INTO @hash, @count;') FROM `information_schema`.`columns` WHERE `table_schema` = DATABASE() AND `table_name` = name INTO _sql; PREPARE stmt FROM _sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET hash = @hash; SET count = @count; END;
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;
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;
\ No newline at end of file