fix(athena): accept a query that ends in a semicolon
Athena runs a statement with or without its closing semicolon, but the query engine wraps it in COPY (...) TO ..., where the semicolon is a syntax error, so SELECT * FROM orders; always failed. Trailing semicolons are now dropped before the statement is handed to the engine.
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user