PostgreSQL Syntax Checker
This postgres syntax checker parses SQL with the PostgreSQL parser itself, compiled to WebAssembly and running in your browser, so it needs neither a database nor a connection string. A rejected statement comes back carrying the token the grammar stopped on, with its line and column.
Press Check. The parser's answer appears here, with the line and column when it stops.
Nothing is uploaded. The parser runs inside this tab, and Ctrl+Enter, Cmd+Enter on a Mac, runs a check.
What it does and how to use it
Paste one statement or a whole migration file and press Check, or run it from the keyboard with Ctrl+Enter (Cmd+Enter on a Mac). Either the grammar accepts it and the tool counts the statements, or it stops and hands back the parser's own message, the position, and the offending line with a caret under the token.
The parser is libpg-query, the PostgreSQL C parser compiled to WebAssembly. About a megabyte arrives on your first Check rather than on page load, so that run is the slow one.
Why you see "syntax error at or near"
That message names the first token the grammar could not place, which is often one past the real mistake. Four causes cover most of the searches landing here.
Reserved words in an identifier position cause the majority. create table user (id int); stops at user, and so do insert into user and select id, order from line_items. Double-quote the name and create table "user" (id int) goes through. Oddly select * from user is accepted, since there user is the SQL value function returning the current role.
Because IF belongs to PL/pgSQL rather than to SQL, a bare IF true THEN ... END IF; fails at character one, and wrapping it in a DO $$ ... $$ block is what the grammar wants.
Hyphens break for a plainer reason. Outside quotes a hyphen is the subtraction operator, which is why select * from my-table; stops at the -. The quoted form, "my-table", keeps the name intact.
Inside a UNION, an arm carrying its own ORDER BY needs parentheses. select 1 order by 1 union select 2; stops at union, while (select 1 order by 1) union (select 2) parses.
None of it says whether your objects exist. The grammar reads the shape of a statement and holds no catalog at all, so a select against a table you never created comes back clean.
Frequently asked questions
Yes. It is libpg-query, the PostgreSQL parser sources compiled to WebAssembly, and every result prints the grammar version it reports, 170007, which is how PostgreSQL writes 17.7.
A regex validator matches whatever patterns its author thought of. Running the grammar instead means dollar-quoted blocks, ON CONFLICT, window functions and MERGE come back clean.
No. A grammar check reads the shape of a statement, never the contents of a catalog, so a misspelled column goes through here and gets caught by the server.
The parser is WebAssembly running inside this tab. Nothing you paste is sent anywhere, and downloading the parser is the only request this page makes.
It is free and there is no account to make. Everything happens in the browser, which leaves no key to request and no quota to hit.
The formatter indents the same statement and makes a long CTE readable again. To actually run it, the playground carries a real PostgreSQL server inside the tab.
Other free PostgreSQL tools
- FormatterIndents a Postgres query and puts the keywords where you expect them, so a long CTE stops being one unreadable line. Minifies it again when you need it back on one line.
- PlaygroundA real Postgres server compiled to WebAssembly, running inside the tab. Create tables, insert rows, join them, and read the plan, with no server behind it.
Further reading: Writing a Postgres seed script that survives the next migration.