Re: Virtual generated columns
jian he <jian.universality@gmail.com>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Expand virtual generated columns for ALTER COLUMN TYPE
- 5069fef1cfae 18.0 landed
-
Eliminate code duplication in replace_rte_variables callbacks
- 363a6e8c6fcf 18.0 landed
-
Expand virtual generated columns in the planner
- 1e4351af329f 18.0 landed
-
Virtual generated columns
- 83ea6c54025b 18.0 landed
-
Additional tests for stored generated columns
- 41084409f635 18.0 landed
-
Improve generated_stored test
- 44b61efb7928 18.0 landed
- 86749ea3b766 18.0 landed
-
Fix handling of CREATE DOMAIN with GENERATED constraint syntax
- 84a67725cd11 18.0 landed
-
Add pg_constraint rows for not-null constraints
- 14e87ffa5c54 18.0 cited
-
Put generated_stored test objects in a schema
- 894be11adfa6 18.0 landed
-
Rename regress test generated to generated_stored
- b9ed4969250d 18.0 landed
-
Small code simplification
- 7ff9afbbd1df 18.0 landed
-
Remove useless code
- e26d313bad92 18.0 landed
-
Remove useless initializations
- da2aeba8f533 18.0 landed
-
doc: Clarify that pg_attrdef also stores generation expressions
- da486d360103 18.0 landed
-
Clean out column-level pg_init_privs entries when dropping tables.
- 76618097a6c0 17.0 cited
-
Re-implement the ereport() macro using __VA_ARGS__.
- e3a87b4991cc 13.0 cited
Attachments
- v1-0001-fix-expand-virtual-generated-column-Var-node-varn.patch (text/x-patch) patch v1-0001
On Mon, Feb 10, 2025 at 11:54 AM Richard Guo <guofenglinux@gmail.com> wrote: > > On Sun, Feb 9, 2025 at 7:02 PM Zhang Mingli <zmlpostgres@gmail.com> wrote: > > On Feb 9, 2025 at 16:00 +0800, Alexander Lakhin <exclusion@gmail.com>, wrote: > > Please look at a planner error with a virtual generated column triggered > > by the following script: > > CREATE TABLE t(a int, b int GENERATED ALWAYS AS (a * 1)); > > > > SELECT SUM(CASE WHEN t.b = 1 THEN 1 ELSE 1 END) OVER (PARTITION BY t.a) > > FROM t AS t1 LEFT JOIN T ON true; > > > > ERROR: XX000: wrong varnullingrels (b) (expected (b 3)) for Var 2/1 > > LOCATION: search_indexed_tlist_for_var, setrefs.c:2901 > > > During the parse stage, we set the Var->varnullingrels in the parse_analyze_fixedparams function. > > Later, when rewriting the parse tree in pg_rewrite_query() to expand virtual columns, we replace the expression column b with a new Var that includes a, since b is defined as a * 1. > > Unfortunately, we overlooked updating the Var->varnullingrels at this point. > > As a result, when we enter search_indexed_tlist_for_var, it leads to a failure. > > While we do have another target entry with the correct varnullingrels, the expression involving the virtual column generates another column reference, which causes the error. > > Currently, I don't have a solid fix. > > One potential solution is to correct the Vars at or after the rewrite stage by traversing the parse tree again using markNullableIfNeeded. > > However, this approach may require exposing the ParseState, which doesn't seem ideal. > > It appears that the virtual column generation function during the rewrite stage does not account for the Var field settings, leading to the errors we are encountering. > > Hmm, would it be possible to propagate any varnullingrels into the > replacement expression in ReplaceVarsFromTargetList_callback()? > in ReplaceVarsFromTargetList_callback, we have ``if (var->varlevelsup > 0)`` ``if (var->varreturningtype != VAR_RETURNING_DEFAULT)`` we can also have. ``if (var->varnullingrels != NULL)`` please check attached. > BTW, I was curious about what happens if the replacement expression is > constant, so I tried running the query below. > > CREATE TABLE t (a int, b int GENERATED ALWAYS AS (1 + 1)); > INSERT INTO t VALUES (1); > INSERT INTO t VALUES (2); > > # SELECT t2.a, t2.b FROM t t1 LEFT JOIN t t2 ON FALSE; > a | b > ---+--- > | 2 > | 2 > (2 rows) > > Is this the expected behavior? I was expecting that t2.b should be > all NULLs. > SELECT t2.a, t2.b FROM t t1 LEFT JOIN t t2 ON FALSE; should be same as SELECT t2.a, 2 as b FROM t t1 LEFT JOIN t t2 ON FALSE; so i think this is expected.