Beta — covers the core PostgreSQL DML and DDL statement set. A number of uncommon top-level statements (e.g.
CREATE CAST,ALTER DOMAIN,CREATE CONVERSION,CREATE STATISTICS,CREATE TEXT SEARCH …,CREATE OPERATOR CLASS,ALTER DEFAULT PRIVILEGES,CREATE / ALTER / DROP DATABASE,CREATE FOREIGN DATA WRAPPER) have no printer yet and are kept exactly as written (case and spacing included) instead. Inside a statement the formatter has a printer for, a construct it can't print raises an explicitUnsupported …error (for example a type modifier that is an expression,foo(1 + 1)) — it never silently drops or changes anything. The test suite enforces this by comparing the PostgreSQL parse tree before and after formatting.
A Prettier plugin for PostgreSQL SQL. Parses SQL with libpg_query (the actual PostgreSQL parser, from PostgreSQL 18) and formats it using Prettier's document IR for consistent, readable output.
- SELECT — column lists, table aliases, all JOIN types (INNER, LEFT, RIGHT, FULL, CROSS, NATURAL), WHERE, GROUP BY / HAVING, ORDER BY, LIMIT / OFFSET, FETCH FIRST … WITH TIES, DISTINCT, DISTINCT ON
- Set operations — UNION / UNION ALL / INTERSECT / EXCEPT, with WITH / ORDER BY / LIMIT applying to the whole result, and operands parenthesized where they need to be
- VALUES — standalone, with WITH, ORDER BY and LIMIT
- Subqueries — correlated subqueries, EXISTS, scalar sublinks,
ARRAY(subquery), row comparisons ((a, b) < (SELECT …)), LATERAL - CTEs — WITH / WITH RECURSIVE, data-modifying CTEs (WITH ... DELETE/INSERT/UPDATE)
- Window functions — OVER (PARTITION BY, ORDER BY, frame clauses: ROWS/RANGE/GROUPS BETWEEN)
- Aggregate functions — FILTER (WHERE ...), ORDER BY inside aggregate (e.g.
string_agg) - GROUP BY extensions — ROLLUP, CUBE, GROUPING SETS
- Locking — FOR UPDATE / FOR SHARE / FOR NO KEY UPDATE / FOR KEY SHARE with OF, NOWAIT, SKIP LOCKED
- INSERT — VALUES (single and multi-row), DEFAULT VALUES, ON CONFLICT DO NOTHING / DO UPDATE SET (conflict targets can be expressions, and a WHERE matches a partial unique index), RETURNING (with
old/newandRETURNING WITH (OLD AS o, NEW AS n), PostgreSQL 18), OVERRIDING USER/SYSTEM VALUE,DEFAULTin VALUES, subscript / field targets (insert into t (a.b)), WITH clause - UPDATE — SET (including multi-column
SET (a, b) = (…)/= (SELECT …),DEFAULT, and subscript / field targets likea[1] = 2), FROM, WHERE (includingWHERE CURRENT OF cursor), RETURNING (withold/new), WITH clause - DELETE — WHERE (including
CURRENT OF), RETURNING (withold/new), WITH clause - TRUNCATE — with RESTART IDENTITY and CASCADE
- Transaction control — BEGIN / START TRANSACTION / COMMIT / ROLLBACK (with AND CHAIN) / SAVEPOINT / RELEASE SAVEPOINT / ROLLBACK TO SAVEPOINT / SET TRANSACTION (isolation level, READ ONLY/WRITE, DEFERRABLE) / PREPARE TRANSACTION / COMMIT PREPARED / ROLLBACK PREPARED
- MERGE — WHEN MATCHED / WHEN NOT MATCHED / WHEN NOT MATCHED BY SOURCE; UPDATE SET, INSERT, DELETE, DO NOTHING actions; conditional AND clause; OVERRIDING in INSERT; RETURNING with
merge_action()andold/new - CALL — stored-procedure invocation
- CREATE TABLE — column definitions with full constraint support: NOT NULL (also named at table level,
CONSTRAINT n NOT NULL col), DEFAULT, PRIMARY KEY, FOREIGN KEY (column-level and table-level, with MATCH FULL/PARTIAL and ON UPDATE/DELETE actions, includingSET NULL (cols)), CHECK (with NO INHERIT), NOT ENFORCED / ENFORCED on CHECK and FOREIGN KEY, UNIQUE (with NULLS NOT DISTINCT), temporal keys (WITHOUT OVERLAPS,PERIOD), EXCLUDE (USING gist (col WITH op, …) WHERE (…)), GENERATED ALWAYS AS (…) STORED / VIRTUAL, GENERATED AS IDENTITY (with sequence options), named constraints, DEFERRABLE / INITIALLY DEFERRED; index parameters (INCLUDE, WITH (…), USING INDEX TABLESPACE);COLLATE, STORAGE and COMPRESSION column clauses, typed tables (OF typewithWITH OPTIONS), type modifiers (VARCHAR(100),NUMERIC(10,2); a modifier that is an expression is unsupported), array types (TEXT[],INT[3]). Types written with SQL keywords get their standard spelling (int→integer); internal names (int4,float8,"char") are kept as written - ALTER TABLE (and INDEX / VIEW / MATERIALIZED VIEW / FOREIGN TABLE / SEQUENCE) — every subcommand libpg_query produces: ADD / DROP COLUMN, ADD / DROP / VALIDATE / ALTER CONSTRAINT (deferrability,
[NOT] ENFORCED,[NO] INHERIT), ALTER COLUMN TYPE … COLLATE … USING, SET / DROP DEFAULT, SET / DROP NOT NULL, identity (ADD GENERATED,SET GENERATED,RESTART,DROP IDENTITY),DROP / SET EXPRESSION, SET STATISTICS / STORAGE / COMPRESSION, SET / RESET (options), trigger and rule enable / disable, row level security, CLUSTER ON, SET LOGGED / UNLOGGED, INHERIT, OF, REPLICA IDENTITY, ATTACH / DETACH PARTITION, OWNER TO, SET TABLESPACE, foreign table OPTIONS - ALTER … RENAME — every object kind: tables (IF EXISTS, ONLY), columns, constraints, indexes, views, sequences, schemas, databases, roles, triggers / policies / rules
ON table, type attributes, domain constraints, operator classesUSING method, functions, aggregates, and more - CREATE VIEW / CREATE MATERIALIZED VIEW
- CREATE TABLE AS —
CREATE TABLE foo AS SELECT ... - CREATE INDEX / CREATE UNIQUE INDEX
- CREATE FUNCTION / PROCEDURE — RETURNS, LANGUAGE, dollar-quoted
$$...$$body, SQL-standard bodies (RETURN expr,BEGIN ATOMIC … END), TRANSFORM FOR TYPE, parameter lists with modes (IN, OUT, INOUT, VARIADIC) and%TYPE - CREATE TYPE — composite (
AS (...)) and enum (AS ENUM (...)) - ALTER TYPE — ADD VALUE for enums (with BEFORE/AFTER placement, IF NOT EXISTS), RENAME VALUE; ADD / DROP / ALTER ATTRIBUTE for composite types
- CREATE / ALTER SEQUENCE — START WITH, INCREMENT BY, MINVALUE, MAXVALUE, CACHE, CYCLE, RESTART WITH
- CREATE SCHEMA — with IF NOT EXISTS and AUTHORIZATION, and the objects created inside it (
CREATE TABLE … CREATE VIEW …) - CREATE EXTENSION — with IF NOT EXISTS
- CREATE TRIGGER — BEFORE/AFTER/INSTEAD OF, INSERT/UPDATE/DELETE/TRUNCATE, FOR EACH ROW/STATEMENT
- DROP — any object: TABLE, VIEW, INDEX (CONCURRENTLY), FUNCTION, TYPE, SCHEMA, …;
TRIGGER / POLICY / RULE … ON table,OPERATOR CLASS / FAMILY … USING method,CAST (a AS b),TRANSFORM FOR type LANGUAGE lang,AGGREGATE agg(*); function and aggregate signatures keep their argument modes and names (f(IN a int, OUT b text),VARIADIC) - GRANT / REVOKE — every object kind (TABLE, SCHEMA, FUNCTION / PROCEDURE with argument modes, SEQUENCE, FOREIGN SERVER, TYPE, DOMAIN, LARGE OBJECT, PARAMETER,
ALL … IN SCHEMA, …); WITH GRANT OPTION; CASCADE - CREATE / ALTER ROLE — with LOGIN, PASSWORD, SUPERUSER, CREATEDB, CREATEROLE, INHERIT, REPLICATION, BYPASSRLS, CONNECTION LIMIT
- COMMENT ON — every object kind, including constraints, triggers, rules and policies
ON table, domain constraints, operator classes / familiesUSING method, transforms, casts, aggregates, large objects (by OID), and routine signatures with argument modes - CREATE TABLE LIKE —
CREATE TABLE new (LIKE existing INCLUDING ALL) - Table partitioning —
PARTITION BY RANGE/LIST/HASH(with expression elements,COLLATEand operator class),CREATE TABLE ... PARTITION OF(with columns and storage; also for foreign tables), partition bounds (FOR VALUES FROM/TO,IN,WITH,DEFAULT) with expressions (date '2020-01-01') - TABLESAMPLE —
FROM t TABLESAMPLE BERNOULLI(10)/SYSTEM(5) REPEATABLE (42) - VACUUM / ANALYZE / CLUSTER / REINDEX — maintenance statements with options, including option values (
VACUUM (PARALLEL 4, INDEX_CLEANUP off)) - CHECKPOINT —
CHECKPOINT - LOAD —
LOAD 'filename' - CREATE / DROP TABLESPACE —
CREATE TABLESPACE name LOCATION path,DROP TABLESPACE [IF EXISTS] name - Foreign data wrappers —
CREATE SERVER,CREATE FOREIGN TABLE(withPARTITION OF,INHERITS, columnOPTIONS),CREATE USER MAPPING,IMPORT FOREIGN SCHEMA - Logical replication —
CREATE / ALTER / DROP PUBLICATIONandSUBSCRIPTION - CREATE AGGREGATE —
CREATE [OR REPLACE] AGGREGATE name (args [ORDER BY …]) (SFUNC = ..., STYPE = ...), including ordered-set andVARIADICarguments - CREATE OPERATOR —
CREATE OPERATOR op (LEFTARG = ..., PROCEDURE = ...) - CREATE COLLATION —
CREATE COLLATION name (LOCALE = ...)andFROM existing - SECURITY LABEL —
SECURITY LABEL FOR provider ON object IS labelfor every object kind
- SQL standard functions —
SUBSTRING(str FROM pattern),EXTRACT(field FROM expr),TRIM(LEADING/TRAILING/BOTH ... FROM str),POSITION(x IN y),expr AT TIME ZONE tz,OVERLAY(...),(a, b) OVERLAPS (c, d),x IS [NOT] NFC NORMALIZED,NORMALIZE(x, NFC),SYSTEM_USER,COLLATION FOR (x) - Collation and literals —
expr COLLATE "C", bit strings (B'101',X'ff'),DEFAULTin VALUES / SET - Type casting —
expr::type(PostgreSQL style),INTERVAL '1 day'literals with optional field modifiers (HOUR TO MINUTE,DAY TO SECOND, etc.) - Operators — schema-qualified
OPERATOR(pg_catalog.+), including in ANY / ALL - Row values and fields —
ROW(a, b),(a, b), field selection(composite).fieldand(f(x)).* - Array subscripts —
arr[1],arr[2:4],arr[:3] - Named arguments —
func(param => value),func(VARIADIC arr) - Ordered-set aggregates —
percentile_cont(0.5) WITHIN GROUP (ORDER BY x) - Conditional — CASE / WHEN / THEN / ELSE, COALESCE, NULLIF, GREATEST, LEAST
- XML functions —
XMLELEMENT(withXMLATTRIBUTES),XMLFOREST,XMLCONCAT,XMLPI,XMLAGG,XMLEXISTS,IS DOCUMENT - XMLTABLE — tabular XML query in the
FROMclause;XMLNAMESPACES, alias column lists,PASSING,COLUMNSwithPATH,DEFAULT,NOT NULL,FOR ORDINALITY - SQL/JSON functions —
JSON_QUERY,JSON_EXISTS,JSON_VALUEwithFORMAT JSON,PASSING,RETURNING,WITH / WITHOUT WRAPPER,KEEP / OMIT QUOTES, and… ON EMPTY/… ON ERROR(includingDEFAULT expr) — PostgreSQL 16+ - SQL/JSON constructors and predicates —
x IS [NOT] JSON [VALUE | ARRAY | OBJECT | SCALAR] [WITH UNIQUE KEYS],JSON,JSON_SCALAR,JSON_SERIALIZE,JSON_OBJECT,JSON_ARRAY(alsoJSON_ARRAY(SELECT …)),JSON_OBJECTAGG,JSON_ARRAYAGGwithFORMAT JSONvalues,ABSENT / NULL ON NULL,WITH UNIQUE KEYS,RETURNING, and aggregateORDER BY/FILTER/OVER— PostgreSQL 16+ - JSON_TABLE — tabular JSON query in the
FROMclause;PASSING,PATH,EXISTS PATH,FORMAT JSON PATH,NESTED PATH … AS name,FOR ORDINALITY, wrapper and quotes options,ON EMPTY/ON ERROR— PostgreSQL 16+ - Predicates — IN / NOT IN, BETWEEN / NOT BETWEEN, LIKE / NOT LIKE, ILIKE / NOT ILIKE, SIMILAR TO, IS NULL / IS NOT NULL, IS DISTINCT FROM, ANY / ALL
- SQL value functions — CURRENT_DATE, CURRENT_TIMESTAMP, CURRENT_USER, SESSION_USER, LOCALTIME, LOCALTIMESTAMP, and others, with precision (
CURRENT_TIMESTAMP(0)) - GROUPING() —
GROUPING(col)predicate used alongside GROUPING SETS
- SET / SHOW / RESET —
SET search_path = myschema,SET TIME ZONE INTERVAL '1' HOUR TO MINUTE,SHOW work_mem,RESET ALL - ALTER SYSTEM —
ALTER SYSTEM SET param = value,ALTER SYSTEM RESET [ALL]— writes topostgresql.conf - DISCARD —
DISCARD ALL,DISCARD PLANS,DISCARD SEQUENCES,DISCARD TEMP - SELECT INTO —
SELECT ... INTO [TEMP | UNLOGGED] table - COPY —
COPY table FROM/TO,COPY (query) TO; program and option list - EXPLAIN —
EXPLAIN,EXPLAIN ANALYZE,EXPLAIN (ANALYZE, VERBOSE, FORMAT JSON) stmt - PREPARE / EXECUTE / DEALLOCATE — server-side prepared statements
- LISTEN / UNLISTEN / NOTIFY — async pub/sub with optional payload
- LOCK TABLE —
LOCK TABLE t IN ACCESS EXCLUSIVE MODE [NOWAIT] - Cursors —
DECLARE CURSOR,FETCH,MOVE,CLOSE - Comment preservation — a script made only of comments is kept; line comments (
-- ...) and block comments (/* ... */) are preserved: leading comments before a statement stay before it; inline trailing comments stay on the statement's final line - Meaning preserved — the test suite checks that formatting never changes what a statement means: every fixture's PostgreSQL parse tree must be the same before and after
- ALTER GROUP / ALTER LARGE OBJECT —
ADD USER/DROP USERmembers, large object OIDs,OWNER TO current_user - ALTER FUNCTION — SET COST, SET ROWS, SET VOLATILE/STABLE/IMMUTABLE, RENAME TO, OWNER TO, SET SCHEMA
- REFRESH MATERIALIZED VIEW — with CONCURRENTLY
- CREATE RULE — BEFORE/AFTER/INSTEAD, INSERT/UPDATE/DELETE/SELECT, DO ALSO/INSTEAD
- Row Security Policies —
CREATE / ALTER POLICYwith USING and WITH CHECK - REASSIGN OWNED —
REASSIGN OWNED BY old_role TO new_role - DROP OWNED —
DROP OWNED BY roles [CASCADE]
| Feature | Notes |
|---|---|
| PL/pgSQL | Full procedural language (IF/ELSIF, LOOP, RETURN, EXCEPTION, DECLARE) — out of scope for a SQL formatter |
| Requirement | Version |
|---|---|
| Node.js | 20 or later |
| .NET Runtime | 8.0 or later |
| Prettier | 3.x |
npm install --save-dev prettier prettier-plugin-postgresqlThen add the plugin to your Prettier configuration:
// prettier.config.js
export default {
plugins: ['prettier-plugin-postgresql'],
overrides: [
{
files: ['*.sql', '*.pgsql'],
options: {
parser: 'pgsql',
},
},
],
};See Getting Started for VS Code setup and build-from-source instructions.
Input (unformatted):
SELECT id,title,price,author_id FROM books WHERE in_stock=TRUE AND price<50 ORDER BY price ASC;Output (default options — lowercase keywords, standard density, trailing commas):
select
id,
title,
price,
author_id
from books
where
in_stock = true
and price < 50
order by price asc;Three formatting options are available. See Options for full details and examples.
| Option | Values | Default | Description |
|---|---|---|---|
sqlKeywordCase |
lower | upper | preserve |
lower |
Case for SQL keywords |
sqlDensity |
compact | standard | spacious |
standard |
Whitespace density |
sqlCommaStyle |
trailing | leading |
trailing |
Comma placement in lists |
// prettier.config.js
export default {
plugins: ['prettier-plugin-postgresql'],
overrides: [
{
files: '*.sql',
options: {
parser: 'pgsql',
sqlKeywordCase: 'upper',
sqlDensity: 'standard',
sqlCommaStyle: 'trailing',
},
},
],
};- Getting Started — installation, VS Code setup, building from source
- Options — all formatting options with examples
- Examples — before/after formatting examples for common patterns
- Formatting Reference — comprehensive formatting rules by statement type