PostgreSQL码农集散地

PostgreSQL 18 支持虚拟生成列(Virtual Generated Columns)

PostgreSQL 18 支持虚拟生成列(Virtual Generated Columns)

这个 patch 为 PostgreSQL 引入了虚拟生成列(Virtual Generated Columns)的功能。与现有的存储生成列(Stored Generated Columns)不同,虚拟生成列在读取时计算(类似于视图),而不是在写入时计算(类似于物化视图)。虚拟生成列的语法为:


... GENERATED ALWAYS AS (...) VIRTUAL

其中,VIRTUAL 是默认选项,与许多其他 SQL 产品保持一致(SQL 标准未对此进行规定)。虚拟生成列在元组中存储为 NULL 值,以节省空间并避免因完全缺失列而导致的潜在问题。

  • Virtual generated columns, 类似视图, 读时计算, 不消耗存储.
  • stored generated columns, 类似物化视图, 写时计算, 并消耗存储.

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=83ea6c54025bea67bcd4949a6d58d3fc11c3e21b


Virtual generated columns  

author  Peter Eisentraut <[email protected]>   
Fri, 7 Feb 2025 08:09:34 +0000 (09:09 +0100)  
committer Peter Eisentraut <[email protected]>   
Fri, 7 Feb 2025 08:46:59 +0000 (09:46 +0100)  
commit  83ea6c54025bea67bcd4949a6d58d3fc11c3e21b  
tree  5a5d13a9f27cd08958d821656086dd1c054516f5  tree  
parent  cbc127917e04a978a788b8bc9d35a70244396d5b  commit | diff  
Virtual generated columns  

This adds a new variant of generated columns that are computed on read
(like a view, unlike the existing stored generated columns, which are  
computed on write, like a materialized view).  

The syntax for the column definition is  

    ... GENERATED ALWAYS AS (...) VIRTUAL  

and VIRTUAL is also optional.  VIRTUAL is the default rather than  
STORED to match various other SQL products.  (The SQL standard makes  
no specification about this, but it also doesn't know about VIRTUAL or  
STORED.)  (Also, virtual views are the default, rather than  
materialized views.)  

Virtual generated columns are stored in tuples as null values.  (A  
very early version of this patch had the ambition to not store them at  
all.  But so much stuff breaks or gets confused if you have tuples  
where a column in the middle is completely missing.  This is a  
compromise, and it still saves space over being forced to use stored  
generated columns.  If we ever find a way to improve this, a bit of  
pg_upgrade cleverness could allow for upgrades to a newer scheme.)  

The capabilities and restrictions of virtual generated columns are  
mostly the same as for stored generated columns.  In some cases, this  
patch keeps virtual generated columns more restricted than they might  
technically need to be, to keep the two kinds consistent.  Some of  
that could maybe be relaxed later after separate careful  
considerations.  

Some functionality that is currently not supported, but could possibly  
be added as incremental features, some easier than others:  

- index on or using a virtual column  
- hence also no unique constraints on virtual columns  
- extended statistics on virtual columns  
- foreign-key constraints on virtual columns  
- not-null constraints on virtual columns (check constraints are supported)  
- ALTER TABLE / DROP EXPRESSION  
- virtual column cannot have domain type  
- virtual columns are not supported in logical replication  

The tests in generated_virtual.sql have been copied over from  
generated_stored.sql with the keyword replaced.  This way we can make  
sure the behavior is mostly aligned, and the differences can be  
visible.  Some tests for currently not supported features are  
currently commented out.  

Reviewed-by: Jian He <[email protected]>  
Reviewed-by: Dean Rasheed <[email protected]>  
Tested-by: Shlok Kyal <[email protected]>  
Discussion: https://www.postgresql.org/message-id/flat/[email protected]  

例子


CREATE TABLE gtest0 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (55) VIRTUAL);  
   6 CREATE TABLE gtest1 (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTUAL);  
   7 SELECT table_name, column_name, column_default, is_nullable, is_generated, generation_expression FROM information_schema.columns WHERE table_schema = 'generated_virtual_tests' ORDER BY 1, 2;  
   8  table_name | column_name | column_default | is_nullable | is_generated | generation_expression   
   9 ------------+-------------+----------------+-------------+--------------+-----------------------  
  10  gtest0     | a           |                | NO          | NEVER        |   
  11  gtest0     | b           |                | YES         | ALWAYS       | 55  
  12  gtest1     | a           |                | NO          | NEVER        |   
  13  gtest1     | b           |                | YES         | ALWAYS       | (a * 2)  
  14 (4 rows)  


-- generation expression must be immutable  
  60 CREATE TABLE gtest_err_4 (a int PRIMARY KEY, b double precision GENERATED ALWAYS AS (random()) VIRTUAL);  
  61 ERROR:  generation expression is not immutable  
  62 -- ... but be sure that the immutability test is accurate  

  47 -- a whole-row var is a self-reference on steroids, so disallow that too  
  48 CREATE TABLE gtest_err_2c (a int PRIMARY KEY,  
  49     b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) VIRTUAL);  
  50 ERROR:  cannot use whole-row variable in column generation expression  
  51 LINE 2:     b int GENERATED ALWAYS AS (num_nulls(gtest_err_2c)) VIRT...  
  52                                                  ^  
  53 DETAIL:  This would cause the generated column to depend on its own value.  


CREATE TABLE gtest_err_2b (a int PRIMARY KEY, b int GENERATED ALWAYS AS (a * 2) VIRTUAL, c int GENERATED ALWAYS AS (b * 3) VIRTUAL);  
  43 ERROR:  cannot use generated column "b"in column generation expression  
  44 LINE 1: ...YS AS (a * 2) VIRTUAL, c int GENERATED ALWAYS AS (b * 3) VIR...  
  45                                                              ^  
  46 DETAIL:  A generated column cannot reference another generated column.