fix(athena): run and list queries in their workgroup #16

Merged
Zakaria merged 3 commits from fix/athena-workgroup-queries into main 2026-10-25 23:44:19 +00:00
2 changed files with 26 additions and 1 deletions
Showing only changes of commit ac96c6451e - Show all commits
@@ -59,6 +59,7 @@ public class AthenaService {
public static final String DEFAULT_CATALOG = "AwsDataCatalog"; public static final String DEFAULT_CATALOG = "AwsDataCatalog";
private static final String DEFAULT_OUTPUT_BUCKET = "floci-athena-results"; private static final String DEFAULT_OUTPUT_BUCKET = "floci-athena-results";
private static final String DEFAULT_WORKGROUP = "primary"; private static final String DEFAULT_WORKGROUP = "primary";
private static final Pattern TRAILING_SEMICOLONS = Pattern.compile("[\\s;]+$");
private static final String DEFAULT_ENGINE_VERSION = "Athena engine version 3"; private static final String DEFAULT_ENGINE_VERSION = "Athena engine version 3";
private static final long MIN_BYTES_SCANNED_CUTOFF_PER_QUERY = 10_000_000L; private static final long MIN_BYTES_SCANNED_CUTOFF_PER_QUERY = 10_000_000L;
private static final String WORKGROUP_RESOURCE = "workgroup/"; private static final String WORKGROUP_RESOURCE = "workgroup/";
@@ -191,7 +192,7 @@ public class AthenaService {
if (outputLocation != null) { if (outputLocation != null) {
ensureOutputBucket(outputLocation); ensureOutputBucket(outputLocation);
} }
duckClient.execute(query, setupDdl, outputLocation); duckClient.execute(withoutTrailingSemicolons(query), setupDdl, outputLocation);
return null; return null;
}).onSuccess(v -> { }).onSuccess(v -> {
markSucceeded(id, execution); markSucceeded(id, execution);
@@ -555,6 +556,15 @@ public class AthenaService {
return properties; return properties;
} }
/**
* Athena accepts one statement with or without a closing semicolon. The engine wraps the
* statement in {@code COPY (...) TO ...}, where a semicolon is a syntax error, so it is
* dropped before the query is handed over.
*/
static String withoutTrailingSemicolons(String query) {
return query == null ? null : TRAILING_SEMICOLONS.matcher(query).replaceFirst("");
}
private static boolean producesResultRows(String query) { private static boolean producesResultRows(String query) {
return RESULT_STATEMENT_PATTERN.matcher(statementText(query)).find(); return RESULT_STATEMENT_PATTERN.matcher(statementText(query)).find();
} }
@@ -0,0 +1,15 @@
package io.github.hectorvent.floci.services.athena;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class AthenaTrailingSemicolonTest {
@Test
void dropsTheClosingSemicolonsAthenaAccepts() {
assertEquals("SELECT * FROM orders LIMIT 10", AthenaService.withoutTrailingSemicolons("SELECT * FROM orders LIMIT 10;"));
assertEquals("SELECT 1", AthenaService.withoutTrailingSemicolons("SELECT 1 ; ;\n"));
assertEquals("SELECT ';' AS s", AthenaService.withoutTrailingSemicolons("SELECT ';' AS s"));
}
}