Runs a SQL query against an AWS Athena workgroup/database (tables registered in the Glue Data
Catalog), replacing the discontinued-by-AWS S3 Select approach. Credentials come from the shared
ec-aws-config node rather than a dedicated per-node config node.
Configuration
- AWS Config
- Reference to the shared
ec-aws-config node that holds the region and
credentials (IAM role or access keys). Required.
- Database
- The Glue Data Catalog database the query resolves unqualified table names against — it
becomes Athena's
QueryExecutionContext.Database. Leave it blank only when every
table in the SQL is written fully qualified as database.table. A query that
references an unqualified table with no Database set fails on Output 2 with
errorType: "Query Failed". Overridable per message via
msg.payload.database.
- Data Catalog
- The data catalog that contains the database — Athena's
QueryExecutionContext.Catalog. Almost always left empty, which means
AwsDataCatalog, the account's built-in Glue catalog for this region. Set an
explicit name only if you have registered an additional catalog (a Hive metastore, or an
Athena federated-query connector such as a JDBC or DynamoDB source) and want to query
through it. An unknown catalog name fails on Output 2 with errorType:
"Query Failed" or "Not Found". Not overridable per message.
- Workgroup
- The Athena workgroup the query runs in. A workgroup is Athena's unit of isolation and
governance: it carries the default query-result S3 location, the per-query and per-workgroup
data-scan limits, result-encryption settings, the CloudWatch metrics namespace, and the
cost tag the scan is billed under.
primary is the default workgroup every
account has. If the chosen workgroup has "Override client-side settings" enabled,
its own result location and encryption win over whatever this node sends. Overridable per
message via msg.payload.workgroup.
- Output Location
- An
s3://bucket/prefix/ URI. Athena always writes the raw result set (plus a
.metadata file) to S3 first; this node then reads it back via
GetQueryResults. This field sets
ResultConfiguration.OutputLocation on StartQueryExecution. It is
required unless the selected workgroup already defines a default result location — and
a workgroup that enforces its settings ignores this value entirely. The credentials on the
AWS Config node need s3:PutObject and s3:GetObject (and usually
s3:GetBucketLocation) on that path. With no location at all, Athena rejects the
request up front and Output 2 carries errorType: "Invalid Request"; a location
the credentials can't write to fails the running query, so Output 2 carries
errorType: "Query Failed". Overridable per message via
msg.payload.outputLocation.
- Max Rows
- Caps how many rows this node reads back from the result set (default 1000); on hitting
the cap it stops paging and sets
payload.truncated: true. It does not
limit what Athena scans or bills — use a LIMIT in the SQL for that.
Writing the query
The SQL Query field is a syntax-highlighting editor. Full Presto/Trino SQL is supported —
joins, sub-queries, GROUP BY, aggregates. The Examples dropdown loads a starter
query you can adapt.
Parameterised queries
Author the query once with tokens and let the per-run values arrive on
msg.payload.queryParams. There are two token forms:
{{name}} — a bound value. It is sent to Athena as a positional bind
parameter (? / ExecutionParameters) and never spliced into
the SQL text, so an upstream (or end-user-influenced) value in a value position cannot change
the query. Use it everywhere a literal would go — right of =, inside
LIKE, IN, BETWEEN, … — and write it without
surrounding quotes: WHERE state = {{st}}, not = '{{st}}'.
{{= name}} — a raw identifier (table or column name). Athena cannot
bind identifiers, so this token is substituted into the text — and is therefore
checked against a strict allowlist (letters, digits, underscore; dot-qualified allowed).
Anything else routes to Output 2 (errorType: "Invalid Identifier") before
Athena runs.
Configured query:
SELECT * FROM {{= tbl}}
WHERE state = {{st}} AND age > CAST({{minAge}} AS INTEGER)
msg.payload.queryParams = { tbl: "patients", st: "TX", minAge: "18" }
→ SELECT * FROM patients WHERE state = ? AND age > CAST(? AS INTEGER)
ExecutionParameters: ["TX", "18"]
Tokens accept nested paths ({{patient.id}}). All bind values are sent to Athena as
strings and cast per column context — wrap the token in CAST(… AS <type>)
when a non-text comparison needs an explicit type. An empty-string value is rejected up front
(Athena will not bind it). Any token with no matching entry in queryParams routes the
message to Output 2 (errorType: "Unresolved Placeholders") before Athena
is called. Parameterised queries require Athena engine v3.
Inputs
- payload.queryParams object
- Name → value map.
{{name}} tokens become bound parameters;
{{= name}} tokens are substituted as identifiers after an allowlist check.
- payload.queryString string
- Overrides the configured SQL query for this call (tokens in it are still compiled the same way).
- payload.database string
- Overrides the configured database for this call.
- payload.workgroup string
- Overrides the configured workgroup for this call.
- payload.outputLocation string
- Overrides the configured S3 output location for this call.
Output 1 — success
- payload.rows array
- Matching rows as plain objects keyed by column name.
- payload.columns array
- Column names, in result order.
- payload.count number
- Number of rows returned.
- payload.truncated boolean
true if the Max Rows cap stopped result fetching before Athena's result set was fully paged through.
- payload.stats object
dataScannedBytes / executionTimeMs, as reported by Athena.
Output 2 — error
- payload.error / errorType / solution / awsCode
- Every failure routes here rather than throwing — unresolved placeholders
(
"Unresolved Placeholders"), an identifier token that isn't a bare name
("Invalid Identifier"), a rejected request such as a missing output location
("Invalid Request"), a query that ran but failed ("Query Failed"),
cancellation, polling timeout ("Query Timeout"), throttling, and bad/insufficient
credentials.
IAM requirements
athena:StartQueryExecution, athena:GetQueryExecution, athena:GetQueryResults,
s3:GetObject/s3:PutObject on the query result bucket, and
glue:GetTable/glue:GetDatabase when querying a Glue Data Catalog table.
Known limitations
{{name}} values are bound via Athena ExecutionParameters, which
requires Athena engine v3 — on engine v2 a query that carries a value token fails on
Output 2. Identifier tokens ({{= name}}) are still substituted textually
(Athena cannot bind identifiers) and are allowlist-checked instead.
- All bind values are passed as strings; Athena casts per column context. A non-text
comparison may need
CAST(? AS <type>) written into the SQL.
- Polling for query completion is bounded (default ~60 attempts at 1s) — a very long-running query surfaces as a timeout on Output 2 even though it may still finish in Athena.
- Max Rows bounds result fetching, not Athena's own scan — use a
LIMIT in the query to control scan cost/latency.