An ANSI-strict dialect
Trino follows the SQL standard closely, and its quoting rules are simpler than in most warehouses:
'...'is always a string, and an embedded quote is doubled:'it''s'. Backslash has no special meaning, so'C:\data\'is a complete literal."..."is always an identifier."order id"or"hive"."sales"."orders"stay exactly as written.- Backticks are not valid at all, so a query carried over from Spark or Hive with
hive.salesfails with a “not valid Trino / Presto syntax” error. U&'caf\00E9'Unicode strings andX'65683F'binary literals are read as single tokens.?is the parameter marker forPREPARE ... FROMandEXECUTE ... USING; both statements format normally.
This matters for formatting. If you picked Spark SQL by mistake, "EU" would look like a string and backticks would be accepted, and a query the Trino parser later rejects could pass through looking fine. The reverse catches a real bug: in Trino mode region = "EU" formats cleanly, but Trino will read "EU" as a column and report that it cannot be resolved.
Federated queries, arrays and lambdas
Three-part names like hive.sales.orders joined to postgresql.crm.customers are kept whole, so a federated join reads like any other. Complex types are recognised: ARRAY['sku', 'qty'], ROW(id BIGINT, amount DOUBLE) and the type names inside CAST follow the keyword-case setting, while map(...), element_at and cardinality are treated as ordinary functions. Long nested calls are broken over several lines with their arguments indented.
Lambda expressions keep their arrows: transform(array_agg(amount), x -> round(x * 1.2, 2)) and reduce(amounts, 0, (s, x) -> s + x, s -> s) stay on one line when they fit.
CREATE TABLE ... WITH (partitioning = ARRAY[...], format = 'PARQUET') AS SELECT puts the table properties in an indented block. OFFSET ... FETCH FIRST n ROWS ONLY, WITH RECURSIVE, EXPLAIN ANALYZE, SHOW and DESCRIBE are all supported.
Layout quirks to expect
CROSS JOIN UNNEST(o.skus) WITH ORDINALITY AS t(sku, line_no)is split afterWITH, as if it began a CTE. The result is still valid; join the two lines by hand if you prefer.- An aggregate filter,
sum(amount) FILTER (WHERE status = 'paid'), is expanded into a small WHERE block inside the brackets. - In
MATCH_RECOGNIZE, quantifiers get a space (b+becomesb +), which Trino’s parser ignores. - Column names that are also Trino keywords, such as
path, get upper-cased along with real keywords. :nameplaceholders are not Trino syntax and get mangled:= :regionturns into=: region. Use?instead.
Like every mode, the formatter checks quotes, comments and brackets precisely, but it is not a full Trino parser; a query can format cleanly and still fail on a missing column or a type mismatch.
Getting readable output for Athena and Presto
Athena engine versions 2 and 3 are built on Presto and Trino, so this mode suits them. Dashboards often store queries as one long line; paste it and format to see the structure. For review-friendly diffs, choose Commas: Leading and AND / OR: Start of line. Use Function case: lower if you want built-ins like approx_percentile and date_trunc to look consistent next to upper-case keywords. More tips are in the SQL style guide.
Examples
Number the items in each order with UNNEST
The UNNEST join is indented under FROM; note the line break that the formatter inserts after WITH.
select o.order_id, t.sku, t.line_no from hive.sales.orders o cross join unnest(o.skus) with ordinality as t(sku, line_no) where o.order_date >= date '2026-01-01'SELECT
o.order_id,
t.sku,
t.line_no
FROM
hive.sales.orders o
CROSS JOIN UNNEST (o.skus)
WITH
ORDINALITY AS t (sku, line_no)
WHERE
o.order_date >= date '2026-01-01'
Federated join across Hive and PostgreSQL catalogs
Catalog-qualified table names stay intact, and the lambda inside transform keeps its arrow.
select c.region, approx_percentile(o.amount, 0.95) as p95_amount, count_if(o.status = 'refunded') as refunds, transform(array_agg(o.amount), x -> round(x * 1.2, 2)) as gross_amounts from hive.sales.orders o join postgresql.crm.customers c on c.customer_id = o.customer_id where o.order_ts >= current_timestamp - interval '30' day group by c.regionSELECT
c.region,
approx_percentile(o.amount, 0.95) AS p95_amount,
count_if(o.status = 'refunded') AS refunds,
transform(array_agg(o.amount), x -> round(x * 1.2, 2)) AS gross_amounts
FROM
hive.sales.orders o
JOIN postgresql.crm.customers c ON c.customer_id = o.customer_id
WHERE
o.order_ts >= current_timestamp - INTERVAL '30' day
GROUP BY
c.region
Iceberg table from a CTAS with properties
The WITH properties become an indented list, and ARRAY is upper-cased with the other keywords.
create table iceberg.analytics.daily_revenue with (partitioning = array['day(order_ts)'], format = 'PARQUET') as select date(order_ts) as day, sum(amount) as revenue from hive.sales.orders group by 1CREATE TABLE iceberg.analytics.daily_revenue
WITH
(
partitioning = ARRAY['day(order_ts)'],
format = 'PARQUET'
) AS
SELECT
date(order_ts) AS day,
sum(amount) AS revenue
FROM
hive.sales.orders
GROUP BY
1
Prepared statement with a ? parameter
The ? marker stays a single token, and PREPARE and EXECUTE format as separate statements.
prepare top_customers from select customer_id, sum(amount) as revenue from hive.sales.orders where region = ? group by customer_id order by revenue desc limit 20;
execute top_customers using 'EU';PREPARE top_customers
FROM
SELECT
customer_id,
sum(amount) AS revenue
FROM
hive.sales.orders
WHERE
region = ?
GROUP BY
customer_id
ORDER BY
revenue DESC
LIMIT
20;
EXECUTE top_customers USING 'EU';
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
"`hive`.`sa" is not valid Trino / Presto syntax | Identifiers are quoted with backticks, as in Hive or Spark SQL. Trino only accepts double quotes. | Replace each backtick with a double quote (“hive”.“sales”.“orders”), or remove the quotes from plain lower-case names. |
This string is never closed — the ' has no matching 'Explained | An apostrophe was escaped with a backslash (‘it's’). Trino has no backslash escapes, so the quote after the backslash ends the string early. | Double the apostrophe: ‘it’‘s’. |
This '[' is never closedExplained | An ARRAY[…] constructor or a subscript like items[1] is missing its closing ]. | Add the ] after the last element or index. |
Unexpected ')' — there is no matching '(' before itExplained | One bracket too many after a nested CAST(ROW(…) AS ROW(…)) or a lambda argument list. | Count the brackets in that expression and remove the extra ) the error points to. |
Frequently asked questions
Does it work for Amazon Athena queries?
Yes. Athena SQL is based on Presto and Trino, so choose the Trino / Presto dialect. Athena DDL that uses Hive syntax with backticks is the exception; it needs double quotes or no quotes to format here.
Is Presto supported as well as Trino?
Yes. The two share the same quoting rules and nearly all syntax, so one dialect setting covers both.
Which parameter style should I use?
Use ? markers, which Trino supports in prepared statements. Named :param markers are not valid Trino SQL and the formatter splits them apart.
Why does the output break the line after WITH in UNNEST ... WITH ORDINALITY?
The underlying formatter treats WITH as the start of a clause. The query still runs the same; you can rejoin the two lines if you like.
Are my queries sent to a server?
No. Formatting runs in the page itself, so catalog names, connection details in comments and data values are never uploaded.