Skip to content

Commit 8b60fb4

Browse files
committed
blog: what’s new in Postgres 18 developer
1 parent 8b63565 commit 8b60fb4

File tree

3 files changed

+188
-4
lines changed

3 files changed

+188
-4
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
---
2+
title: "What's New in PostgreSQL 18 - a Developer's Perspective"
3+
author: Tianzhou
4+
updated_at: 2025/09/26 12:00
5+
feature_image: /content/blog/what-is-new-in-postgres-18-for-developer/cover.webp
6+
tags: Industry
7+
featured: true
8+
description: "Overview of PostgreSQL 18 features from a developer's perspective"
9+
---
10+
11+
PostgreSQL 18 was officially released on [September 25, 2025](https://www.postgresql.org/about/news/postgresql-18-released-3142/). No doubt the most consequential change is the new Asynchronous I/O subsystem. However, this post focuses on the features that will impact developers in their day-to-day work, starting with native UUID v7 support.
12+
13+
## Native UUID v7 Support
14+
15+
- Commit: [78c5e141e](https://postgr.es/c/78c5e141e)
16+
- Docs: https://www.postgresql.org/docs/18/datatype-uuid.html
17+
18+
PostgreSQL 18 introduces the `uuidv7()` function for generating UUID version 7 values. This native support almost closes the long-standing debate about whether to use `SERIAL/IDENTITY` types or UUIDs as primary keys. UUIDv7 combines the best of both worlds: the global uniqueness of UUIDs with the sequential nature that makes serial keys perform well.
19+
20+
```sql
21+
-- Generate a UUIDv7
22+
SELECT uuidv7();
23+
24+
-- Create a table using UUIDv7 as primary key
25+
CREATE TABLE orders (
26+
id UUID PRIMARY KEY DEFAULT uuidv7(),
27+
customer_id INT,
28+
total DECIMAL(10,2),
29+
created_at TIMESTAMP DEFAULT NOW()
30+
);
31+
```
32+
33+
Previously, while PostgreSQL extensions could generate UUIDv7, the most widely used `uuid-ossp` extension doesn't implement v7. Lesser-known extensions that do support v7 aren't typically packaged with PostgreSQL distributions, especially on cloud providers. This meant developers wanting UUIDv7 had to implement generation logic in their applications. Native support removes this friction entirely.
34+
35+
## VIRTUAL Generated Columns
36+
37+
- Commit: [83ea6c540](https://postgr.es/c/83ea6c540)
38+
- Docs: https://www.postgresql.org/docs/18/sql-createtable.html#SQL-CREATETABLE-PARMS-GENERATED-STORED
39+
40+
Generated columns were first introduced in PostgreSQL 12, but only with the `STORED` option, which means the generated column is stored on write and occupies disk space. Adding a `STORED` generated column will rewrite the entire table.
41+
42+
PostgreSQL 18 introduces the `VIRTUAL` option and makes it the default behavior for generated columns. These columns compute their values when read rather than when written. This is a more common approach, and making it the default aligns PostgreSQL with other major databases.
43+
44+
```sql
45+
-- Create table with virtual generated column
46+
CREATE TABLE employees (
47+
id SERIAL PRIMARY KEY,
48+
first_name TEXT NOT NULL,
49+
last_name TEXT NOT NULL,
50+
salary DECIMAL(10,2),
51+
-- You can also skip VIRTUAL since it's the default option
52+
annual_salary DECIMAL(12,2) GENERATED ALWAYS AS (salary * 12) VIRTUAL
53+
);
54+
55+
-- Insert data (generated columns are computed automatically)
56+
INSERT INTO employees (first_name, last_name, salary) VALUES
57+
('John', 'Doe', 5000.00),
58+
('Jane', 'Smith', 6500.00);
59+
60+
SELECT first_name, last_name, salary, annual_salary
61+
FROM employees;
62+
```
63+
64+
Virtual column advantages:
65+
66+
- **Space Saving**: For derived data that doesn't need physical storage, virtual columns eliminate redundant disk usage.
67+
68+
- **Dynamic Computation**: Some computations benefit from being executed at query time, especially when they depend on current system state.
69+
70+
On the other hand, `STORED` generated columns remain valuable for heavy computations that rarely change, where the performance cost of repeated calculation outweighs storage concerns.
71+
72+
However, there are certain limitations as mentioned in the commit. The most noticeable one is that you cannot create indexes on `VIRTUAL` generated columns.
73+
74+
PostgreSQL 18 also adds support for generated columns in logical replication ([commit](https://postgr.es/c/745217a05), [docs](https://www.postgresql.org/docs/18/logical-replication-gencols.html)). This feature is restricted to `STORED` generated columns only.
75+
76+
## OLD and NEW Values in RETURNING
77+
78+
- Commit: [80feb727c](https://postgr.es/c/80feb727c)
79+
- Docs: https://www.postgresql.org/docs/current/dml-returning.html
80+
81+
PostgreSQL 18 enhances the `RETURNING` clause to access both old and new row values in DML operations. This eliminates the need for separate queries to capture before/after values in audit logs.
82+
83+
**UPDATE Operations**
84+
85+
```sql
86+
-- Update prices and see both old and new values
87+
UPDATE products
88+
SET price = price * 1.10
89+
WHERE price <= 99.99
90+
RETURNING
91+
name,
92+
old.price AS old_price,
93+
new.price AS new_price,
94+
new.price - old.price AS price_change;
95+
```
96+
97+
**INSERT with ON CONFLICT**
98+
99+
```sql
100+
-- Upsert with change tracking
101+
INSERT INTO products (name, price) VALUES ('Widget', 25.00)
102+
ON CONFLICT (name) DO UPDATE SET price = EXCLUDED.price
103+
RETURNING
104+
name,
105+
old.price AS previous_price,
106+
new.price AS current_price,
107+
(old.price IS NULL) AS is_new_record;
108+
```
109+
110+
**DELETE Operations**
111+
112+
```sql
113+
-- Track what was deleted
114+
DELETE FROM products
115+
WHERE price < 10.00
116+
RETURNING
117+
old.name AS deleted_product,
118+
old.price AS deleted_price;
119+
```
120+
121+
## EXPLAIN ANALYZE with BUFFERS by Default
122+
123+
- Commit: [c2a4078eb](https://postgr.es/c/c2a4078eb)
124+
- Docs: https://www.postgresql.org/docs/current/sql-explain.html
125+
126+
PostgreSQL 18 improves the `EXPLAIN` command to provide more detailed query execution information automatically.
127+
128+
`EXPLAIN ANALYZE` now includes buffer usage information by default, eliminating the need for the `BUFFERS` option. This default behavior helps developers spot I/O performance issues that would otherwise be overlooked:
129+
130+
```sql
131+
postgres=# EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25;
132+
133+
-- PostgreSQL 18: BUFFERS included automatically
134+
Seq Scan on users (cost=0.00..18.75 rows=125 width=64) (actual time=0.029..0.087 rows=178 loops=1)
135+
Filter: (age > 25)
136+
Rows Removed by Filter: 89
137+
Buffers: shared hit=12
138+
Planning:
139+
Buffers: shared hit=156 read=3
140+
I/O Timings: shared read=0.024
141+
Planning Time: 0.512 ms
142+
Execution Time: 0.734 ms
143+
```
144+
145+
## pg_get_acl() for Permission Inspection
146+
147+
- Commit: [4564f1ceb](https://postgr.es/c/4564f1ceb)
148+
- Docs: https://www.postgresql.org/docs/18/functions-info.html#FUNCTIONS-INFO-OBJECT-TABLE
149+
150+
PostgreSQL 18 introduces the `pg_get_acl()` function for programmatically retrieving Access Control Lists (ACLs) for database objects. If you've ever spent time debugging the infamous `ERROR 42501: permission denied` messages, you'll appreciate having a unified way to inspect object privileges.
151+
152+
Previously, troubleshooting permissions required querying different system catalogs (`pg_class`, `pg_proc`, `pg_namespace`, `pg_attribute`) depending on the object type, each with their own ACL format.
153+
154+
The `pg_get_acl()` function provides a unified interface for retrieving Access Control Lists from any database object, eliminating the need to remember which catalog to query for different object types.
155+
156+
```sql
157+
postgres=# SELECT
158+
(pg_identify_object(s.classid,s.objid,s.objsubid)).*,
159+
pg_catalog.pg_get_acl(s.classid,s.objid,s.objsubid) AS acl
160+
FROM pg_catalog.pg_shdepend AS s
161+
JOIN pg_catalog.pg_database AS d
162+
ON d.datname = current_database() AND
163+
d.oid = s.dbid
164+
JOIN pg_catalog.pg_authid AS a
165+
ON a.oid = s.refobjid AND
166+
s.refclassid = 'pg_authid'::regclass
167+
WHERE s.deptype = 'a';
168+
-[ RECORD 1 ]-----------------------------------------
169+
type | table
170+
schema | public
171+
name | testtab
172+
identity | public.testtab
173+
acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
174+
```
175+
176+
While not as headline-grabbing as the new Asynchronous I/O subsystem, these quality-of-life improvements make day-to-day development easier—and clearer interfaces like `pg_get_acl()` benefit not only human developers, but AI agents as well.
177+
178+
## Further Readings
179+
180+
- [Postgres 18 full release notes](https://www.postgresql.org/docs/18/release-18.html)
181+
- [DBA's perspective about Postgres 18](/blog/what-is-new-in-postgres-18)

content/blog/what-is-new-in-postgres-18.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ updated_at: 2025/05/23 12:00
55
feature_image: /content/blog/what-is-new-in-postgres-18/cover.webp
66
tags: Industry
77
featured: true
8-
description: 'Overview of PostgreSQL 18 features with spicy comments'
8+
description: "Overview of PostgreSQL 18 features with spicy comments from a DBA's perspective"
99
---
1010

1111
<HintBlock type="info">
@@ -25,9 +25,7 @@ will constantly update this post to include the latest Postgres 18.x releases.
2525
- **PostgreSQL 18 Beta 2**: July 17, 2025
2626
- **PostgreSQL 18 Beta 3**: August 14, 2025
2727
- **PostgreSQL 18 RC 1**: September 4, 2025
28-
- **PostgreSQL 18 Official Release!**: September 25, 2025
29-
30-
PostgreSQL just announced [18 beta 1](https://www.postgresql.org/about/news/postgresql-18-beta-1-released-3070/). While some features may still be dropped, it's worth taking a look.
28+
- **PostgreSQL 18 Official Release!**: September 25, 2025.
3129

3230
## Asynchronous I/O
3331

@@ -98,3 +96,8 @@ The PostgreSQL 18 Beta 1 announcement highlights a significant improvement: the
9896
Of course, with any major version, there are compatibility changes to be aware of. The release notes for version 18 list several, including changes to time zone abbreviation handling, the deprecation of MD5 password authentication (finally!), changes to how `VACUUM` and `ANALYZE` handle inheritance, and modifications to `COPY FROM` behavior regarding end-of-file markers. It's crucial to review these incompatibilities thoroughly before planning an upgrade.
9997

10098
**DBA Note:** Anything to reduce that terrifying maintenance window during upgrade is welcome. But the list of "incompatibilities" is still there, waiting to trip up the unwary. Anyway, DBAs will still need to read the fine print, test exhaustively in staging, and have a solid rollback plan. Upgrades are still where DBAs earn their hazard pay.
99+
100+
## Further Readings
101+
102+
- [Postgres 18 full release notes](https://www.postgresql.org/docs/18/release-18.html)
103+
- [Developer's perspective about Postgres 18](/blog/what-is-new-in-postgres-18-for-developer)
30.2 KB
Loading

0 commit comments

Comments
 (0)