Redshift is PostgreSQL underneath, with its own additions
Redshift inherits most of its lexical rules from PostgreSQL 8, and PasteKit’s Amazon Redshift mode follows them:
- A single quote inside a string is written twice:
'it''s'. A backslash is not treated as an escape, so'it\'s'is reported as an unterminated string. "table"or"Order Date"in double quotes is an identifier. That is how you query system views with reserved column names, as insvv_table_info.#recent_ordersis read as one name, so temporary tables written with the hash prefix are not split.$1,$2are the positional parameters used by prepared statements and the Data API.@has no special meaning.
Choosing Standard SQL or MySQL instead would change these rules. MySQL mode, for example, reads #recent_orders as the start of a comment, so the rest of that line is never formatted.
DDL with distribution, sort keys and compression
CREATE TABLE statements are where Redshift differs most from other warehouses. The column list is indented one column per line, and arguments get normalised spacing: identity(1,1) comes out as IDENTITY (1, 1) and decimal(12,2) as DECIMAL(12, 2).
Two layout quirks are worth knowing. Each ENCODE clause is moved onto its own line under the column it belongs to, so customer_id INTEGER NOT NULL is followed by ENCODE AZ64 one line down. After the closing bracket, DISTSTYLE, DISTKEY and COMPOUND SORTKEY stay together on one line, and only some of those words are upper-cased (diststyle KEY distkey (...)). The DDL is still valid; tidy the casing by hand if it matters to you.
UNLOAD, COPY and maintenance commands
UNLOAD ('select ...') takes its query as a quoted string, so the inner SQL stays exactly as written, doubled quotes and all. The TO 's3://...' target, IAM_ROLE and FORMAT AS PARQUET follow on the closing line, and PARTITION BY starts a new one. If you want the unloaded query formatted too, paste it separately, format it, and put it back inside the quotes (doubling any single quotes).
COPY sales.orders FROM 's3://...' places FROM on its own line, followed by the credentials and options. ANALYZE and VACUUM format as one-liners, though VACUUM DELETE ONLY is split after VACUUM.
Words that Redshift treats as keywords are upper-cased even when you use them as column names. A column called region becomes REGION because it is also a COPY option. Redshift folds unquoted names to lower case, so this does not change what the query does; switch Keyword case to As written if the capitals bother you.
What will not format, and useful settings
Stored procedures written as CREATE PROCEDURE ... AS $$ ... $$ LANGUAGE plpgsql are rejected with "$$" is not valid Amazon Redshift syntax. Format the SQL statements from the procedure body on their own instead. JDBC-style ? placeholders are rejected too; use $1 or a sample value.
For long revenue and cohort queries, try Commas: Leading with Function case: UPPER, so aggregates like SUM, LISTAGG and DATEADD stand out from column names. Minify is handy before pasting SQL into a Lambda or Glue job string. For general conventions, see the SQL style guide.
Examples
Orders table with DISTKEY, SORTKEY and ENCODE
Columns go one per line, with each ENCODE clause on the line below its column.
create table sales.orders (order_id bigint identity(1,1), customer_id integer not null encode az64, order_date date not null encode az64, status varchar(20) encode zstd, amount decimal(12,2) encode az64) diststyle key distkey (customer_id) compound sortkey (order_date, customer_id)CREATE TABLE sales.orders (
order_id BIGINT IDENTITY (1, 1),
customer_id INTEGER NOT NULL
ENCODE AZ64,
order_date date NOT NULL
ENCODE AZ64,
status VARCHAR(20)
ENCODE ZSTD,
amount DECIMAL(12, 2)
ENCODE AZ64
) diststyle KEY distkey (customer_id) compound sortkey (order_date, customer_id)
UNLOAD a year of orders to S3 as Parquet
The query inside UNLOAD is a string literal, so its doubled ‘’ quotes and lowercase text are left alone.
unload ('select order_id, customer_id, amount, order_date from sales.orders where order_date >= ''2026-01-01''') to 's3://acme-exports/orders/' iam_role 'arn:aws:iam::123456789012:role/RedshiftUnload' format as parquet partition by (order_date)UNLOAD (
'select order_id, customer_id, amount, order_date from sales.orders where order_date >= ''2026-01-01'''
) TO 's3://acme-exports/orders/' IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftUnload' FORMAT AS PARQUET
PARTITION BY
(order_date)
Monthly revenue per customer with running totals
Leading commas and upper-case function names make the nested window aggregate and LISTAGG easy to scan.
select customer_id, date_trunc('month', order_date) as month, sum(amount) as revenue, sum(sum(amount)) over (partition by customer_id order by date_trunc('month', order_date) rows unbounded preceding) as lifetime_revenue, listagg(distinct status, ',') within group (order by status) as statuses, nvl(max(discount_code), 'none') as last_code from sales.orders where order_date >= dateadd(month, -12, getdate()) group by 1, 2SELECT
customer_id
, DATE_TRUNC('month', order_date) AS month
, SUM(amount) AS revenue
, SUM(SUM(amount)) over (
PARTITION BY
customer_id
ORDER BY
DATE_TRUNC('month', order_date) rows unbounded preceding
) AS lifetime_revenue
, LISTAGG(DISTINCT status, ',') within GROUP (
ORDER BY
status
) AS statuses
, NVL(MAX(discount_code), 'none') AS last_code
FROM
sales.orders
WHERE
order_date >= DATEADD(month, -12, GETDATE())
GROUP BY
1
, 2
Temp table, COPY and ANALYZE in one script
The #recent_orders name stays whole, and each statement is separated by a blank line.
create temp table #recent_orders as select order_id, customer_id, amount from sales.orders where order_date >= current_date - 7;
copy sales.orders from 's3://acme-raw/orders/2026/10/' iam_role 'arn:aws:iam::123456789012:role/RedshiftCopy' format as csv ignoreheader 1 region 'us-east-1';
analyze sales.orders;CREATE TEMP TABLE #recent_orders AS
SELECT
order_id,
customer_id,
amount
FROM
sales.orders
WHERE
order_date >= current_date - 7;
COPY sales.orders
FROM
's3://acme-raw/orders/2026/10/' IAM_ROLE 'arn:aws:iam::123456789012:role/RedshiftCopy' FORMAT AS CSV IGNOREHEADER 1 REGION 'us-east-1';
ANALYZE sales.orders;
Common errors and how to fix them
| Error | Cause | Fix |
|---|---|---|
This string is never closed — the ' has no matching 'Explained | A quote was escaped MySQL-style with a backslash, as in ‘it's’. Redshift mode does not treat backslash as an escape, so the literal never ends. | Write the apostrophe as two single quotes: ‘it’‘s’. Inside an UNLOAD query string, every quote in the inner SQL needs doubling. |
"$$" is not valid Amazon Redshift syntax | The input is a stored procedure or function with a dollar-quoted body, which this dialect cannot parse. | Format the statements from inside the body separately, then paste them back between the $$ markers. |
"?" is not valid Amazon Redshift syntax | The query contains ? placeholders from a JDBC or ODBC client. | Use Redshift positional parameters ($1, $2) or temporarily substitute literal values. |
"`orders`" is not valid Amazon Redshift syntax | The query was copied from MySQL or BigQuery and still quotes names with backticks. | Replace backticks with double quotes (“orders”), or drop the quotes if the name is a plain lower-case identifier. |
This '(' is never closedExplained | Often an UNLOAD statement whose closing ) after the quoted query was lost, or an unfinished IDENTITY(1, 1). | Jump to the highlighted bracket and add the missing ) where the expression ends. |
Frequently asked questions
Can it format the query inside UNLOAD?
Not in place, because that query is a string literal and strings are never changed. Format the inner SELECT on its own, then paste it back inside the quotes with every single quote doubled.
Does it support Redshift stored procedures?
No. Dollar-quoted procedure bodies are rejected in Redshift mode. You can still format each statement from the body individually.
Will it break #temp table names?
No. In Redshift mode a leading # is part of the identifier, so #recent_orders is kept whole. Picking MySQL mode by mistake would treat it as a comment.
Why did my region column turn upper case?
REGION is a Redshift keyword (it is a COPY option), so keyword casing applies to it. Unquoted names are case-insensitive in Redshift, so the query behaves the same.
Does my SQL get sent to AWS or a server?
No. Formatting happens entirely in your browser, which matters when queries contain IAM role ARNs or bucket names.