Extensions
- pg_synonym 1.0.0
- CREATE SYNONYM for PostgreSQL
Documentation
- README.pt-BR
- pg_synonym
- CHANGELOG
- Changelog
- COMO_FUNCIONA
- Como o pg_synonym funciona por dentro
README
Contents
pg_synonym
CREATE SYNONYM for PostgreSQL. Give a table or view a second name in another schema, and keep using
it for reads and writes while the real table changes underneath.
🇧🇷 Leia em português
-- SQL Server: CREATE SYNONYM vendas.cliente FOR erp.clientes
SELECT synonym.create('vendas.cliente', 'erp.clientes');
SELECT * FROM vendas.cliente; -- reads erp.clientes
INSERT INTO vendas.cliente (nome) VALUES ('Ana'); -- writes erp.clientes
Why?
SQL Server and Oracle have synonyms. Applications use them to hide where a table really lives, which schema it is in, or what it is called this year. PostgreSQL has no synonyms, so migrations usually fall back on one of two workarounds:
- A view. It works until someone runs
ALTER TABLEon the real table. New columns do not show up,DROP COLUMNandALTER COLUMN TYPEare refused (“other objects depend on it”), andDROP TABLEneedsCASCADE. search_pathtricks. These are global, fragile and invisible in the code.
pg_synonym gives you real synonyms. Each one is backed by an updatable view, and the extension keeps that view
in step with its target automatically, whatever ALTER TABLE throws at it.
Usage
Create and drop
SELECT synonym.create('vendas.cliente', 'erp.clientes');
SELECT synonym.drop('vendas.cliente');
SELECT synonym.drop('vendas.cliente', if_exists => true);
DROP VIEW vendas.cliente, or DROP OWNED BY the synonym’s owner, also drops the synonym.
Names follow the usual PostgreSQL rules. Without a schema, the synonym goes into the first schema of search_path,
and the target is looked up through search_path. Quoted names work: synonym.create('app."Pedidos"', 'erp."Old Orders"').
List
SELECT * FROM synonym.list;
synonym | target | owner | status | created_at
----------------+---------------+-------+----------+-------------------------------
vendas.cliente | erp.clientes | ana | ok | 2026-10-19 15:47:28.03-03
vendas.produto | erp.produtos | ana | pending | 2026-10-19 15:48:02.11-03
| status | meaning |
|---|---|
ok |
Working. |
pending |
The target does not exist yet. The synonym starts working as soon as it is created. |
conflict |
Another object already uses the synonym’s name. Rename or drop it. |
out of sync |
Should not happen. Run SELECT synonym.refresh();. |
Use it like the table
SELECT ... FROM vendas.cliente JOIN ...;
INSERT INTO vendas.cliente (...) VALUES (...) RETURNING id;
INSERT ... ON CONFLICT (id) DO UPDATE ...;
UPDATE vendas.cliente SET ...;
DELETE FROM vendas.cliente WHERE ...;
The target’s defaults, identity columns, constraints, triggers and row-level security all apply. The planner sees straight through to the real table, so queries through a synonym are exactly as fast as queries on the table.
SQL Server → PostgreSQL cheat sheet
| SQL Server | pg_synonym |
|---|---|
CREATE SYNONYM s.x FOR s.y |
SELECT synonym.create('s.x', 's.y') |
DROP SYNONYM s.x |
SELECT synonym.drop('s.x') |
DROP SYNONYM IF EXISTS s.x |
SELECT synonym.drop('s.x', if_exists => true) |
SELECT * FROM sys.synonyms |
SELECT * FROM synonym.list |
CREATE SYNONYM x FOR otherdb.dbo.y |
Not supported. Create a foreign table with postgres_fdw and point the synonym at it |
What it keeps in sync
| You do this to the target | The synonym |
|---|---|
ADD COLUMN |
shows the new column |
RENAME COLUMN |
uses the new name |
ALTER COLUMN ... TYPE |
uses the new type (a plain view would block this) |
DROP COLUMN |
loses the column (a plain view would block this) |
RENAME TO, SET SCHEMA |
keeps pointing at the same table, like a view would |
DROP TABLE |
stays defined and becomes pending (no CASCADE needed) |
| create it again | starts working again, with the new columns |
You can also create a synonym before its target exists (late binding, as in SQL Server). It becomes
pending and comes alive when the target is created.
Views that you build on top of a synonym survive harmless changes such as adding or renaming columns.
Changes that need the synonym rebuilt (DROP COLUMN, ALTER COLUMN TYPE, DROP TABLE) are refused while such
views exist, which is the same thing PostgreSQL does for any view. Use CASCADE where the command accepts it.
Permissions
A synonym never gives anyone more access than they already have to the target.
- Every synonym view is created with
security_invoker = true. The person using the synonym needs the privileges on the target, and the target’s row-level security policies apply to them. - To create a synonym you need
CREATEprivilege on the synonym’s schema, just as for creating a view, andUSAGEon the target’s schema so you can name it. You do not need any privilege on the target table itself. - Only the synonym’s owner, a member of the owner’s role, or a superuser can drop it.
- The table’s owner can still
ALTERorDROPthe table even when other users have synonyms on it.
Installing the extension requires a superuser, because it uses event triggers. Using it does not.
Installation
Requires PostgreSQL 15 or newer. There is nothing to add to shared_preload_libraries and no restart.
Linux (Debian / Ubuntu)
sudo apt install build-essential postgresql-server-dev-17 # use your PostgreSQL major version
git clone https://github.com/Mazuco/pg_synonym.git
cd pg_synonym
make
sudo make install
Linux (Rocky / RHEL / Fedora, PGDG packages)
sudo dnf install -y gcc make git redhat-rpm-config dnf-plugins-core
sudo dnf config-manager --set-enabled crb # Rocky 9 (Rocky 8: --set-enabled powertools)
sudo dnf install -y postgresql17-devel
make PG_CONFIG=/usr/pgsql-17/bin/pg_config with_llvm=no
sudo make install PG_CONFIG=/usr/pgsql-17/bin/pg_config with_llvm=no
macOS (Homebrew)
brew install postgresql@17
make PG_CONFIG=$(brew --prefix postgresql@17)/bin/pg_config
make install PG_CONFIG=$(brew --prefix postgresql@17)/bin/pg_config
Docker (any OS, including Windows)
docker build -t pg_synonym . # PostgreSQL 17 by default
docker build --build-arg PG_MAJOR=18 -t pg_synonym:18 .
docker run -d --name pgsyn -e POSTGRES_PASSWORD=postgres -p 5432:5432 pg_synonym
Build error mentioning
clangorllvm? Your PostgreSQL package was built with JIT support and tries to compile LLVM bitcode, which this extension does not need. Build withmake with_llvm=noand install withsudo make with_llvm=no install.
Enable it in your database
CREATE EXTENSION pg_synonym; -- as a superuser
The extension lives in its own schema, synonym.
Limitations
- Tables, views, materialized views, partitioned tables and foreign tables can be targets. Functions, procedures and sequences cannot (yet).
- No new syntax. PostgreSQL extensions cannot add SQL statements, so
CREATE SYNONYMis a function call. - No cross-database synonyms. Point the synonym at a
postgres_fdwforeign table instead. - A synonym of a synonym is not allowed. SQL Server does not allow it either.
- Things views cannot do do not work through a synonym either:
TRUNCATE,COPY synonym TO(useCOPY (SELECT * FROM synonym) TO), and DDL such asALTER TABLE synonym.MERGEinto a synonym needs PostgreSQL 17 or newer. - Renamed targets are followed. A synonym keeps pointing at its table after
RENAMEorSET SCHEMA, as a view would. SQL Server binds by name instead. - Manage synonyms only through these functions. Renaming a synonym’s view, its schema, or turning off
security_invokerby hand breaks the link. When a synonym is rebuilt, GRANTs you added to it by hand are reset. DROP EXTENSION pg_synonymturns existing synonyms into ordinary views and leaves thesynonymschema behind (DROP SCHEMA synonymremoves it).
Performance
- Queries: no overhead. A synonym is a simple view that the planner removes.
- DDL: the event triggers add work to
CREATE,ALTERandDROPof tables and views. Measured on PostgreSQL 16: about 0.4 ms per DDL with no synonyms, and about 11 ms per DDL with 500 synonyms.
How it works
A synonym is a row in synonym.catalog plus a view CREATE VIEW ... WITH (security_invoker = true) AS SELECT * FROM target.
Event triggers keep them together:
- Before
DROP TABLE,DROP COLUMNorALTER COLUMN TYPEon a target, a small C function reads the command, finds the synonyms involved and drops their views, so PostgreSQL does not refuse the command. - After any DDL on tables, views and schemas, a PL/pgSQL function compares each synonym with its target and creates, extends, renames or rebuilds the view as needed.
- When someone drops a synonym’s view directly (
DROP VIEW,DROP OWNED), the synonym is forgotten.
If the command fails, the whole transaction rolls back, including step 1. Everything except about 200 lines of commented C is
plain PL/pgSQL in pg_synonym--1.0.sql. A line-by-line walkthrough in Portuguese is in
docs/COMO_FUNCIONA.md.
Running the tests
make
sudo make install
make installcheck # needs a running server and a superuser connection
Contributing
Issues and pull requests are welcome. Please add a regression test in test/sql/ and its expected output in
test/expected/ for any change in behaviour.
License
Released under the PostgreSQL License.