public inbox for [email protected]
help / color / mirror / Atom feedFrom: =?utf-8?B?Wml6aHVhbkxpdSBYLU1BTg==?= <[email protected]>
To: =?utf-8?B?amlhbiBoZQ==?= <[email protected]>
To: =?utf-8?B?UGV0ZXIgRWlzZW50cmF1dA==?= <[email protected]>
To: =?utf-8?B?cGdzcWwtaGFja2Vycw==?= <[email protected]>
Cc: =?utf-8?B?Q29yZXkgSHVpbmtlcg==?= <[email protected]>
Cc: =?utf-8?B?VG9tIExhbmU=?= <[email protected]>
Cc: =?utf-8?B?Y2hlbmdwZW5nX3lhbg==?= <[email protected]>
Cc: =?utf-8?B?Z3VvZmVuZ2xpbnV4?= <[email protected]>
Cc: =?utf-8?B?5oiR6Ieq5bex55qE6YKu566x?= <[email protected]>
Subject: Re: support create index on virtual generated column.
Date: Sun, 5 Jul 2026 11:04:09 +0800
Message-ID: <[email protected]> (raw)
In-Reply-To: <CACJufxGhTjcc-z1=ejgtXCk7L3iy+B8aoDAvczaszDM4NPpDVw@mail.gmail.com>
References: <CACJufxGao-cypdNhifHAdt8jHfK6-HX=tRBovBkgRuxw063GaA@mail.gmail.com>
<CALdSSPh1opqDbVSSrrWr40B-3TS+q8EfGRLqJRVw+j15X6nPDQ@mail.gmail.com>
<CACJufxGu3jjiyKqPRKP+n92-7u3S_yexCLe1NCQywe96jQxzrg@mail.gmail.com>
<CALdSSPiMrRhSyQvAqGxgLMdcmFLrXSDAsCt32pg=e2bh2k4_Tg@mail.gmail.com>
<CACJufxFx2zmTnTrGaxfNpRpy3RbY9wpjBXqvfP5dWoxsW9f_FQ@mail.gmail.com>
<CACJufxGgkH0PyyqP6ggqcEWHxZzmkV=puY8ad=s8kisss9MAwg@mail.gmail.com>
<CACJufxFBJa2_0jm7q7ZhS5Y=ZTU_b+5YHdQ6vK4Dp8xChUj3Ww@mail.gmail.com>
<CADkLM=dtJZTrbUdFq5UznRepSoYpB2ARm4Q6yGvcgPrxwTtMrQ@mail.gmail.com>
<[email protected]>
<CADkLM=c09mZi_XFix5EyHXPz1Z0k7=-DBV+iAOicCrDiqCqFrw@mail.gmail.com>
<CACJufxEygt-OkS9qSCshNu+fq4533AA0BCBtboW9Mi3xujp=Uw@mail.gmail.com>
<[email protected]>
<CACJufxGhTjcc-z1=ejgtXCk7L3iy+B8aoDAvczaszDM4NPpDVw@mail.gmail.com>
>On Fri, Mar 13, 2026 at 10:01?PM Peter Eisentraut <[email protected]> wrote:
>>
>> I think you could do a much simpler initial version of this if you just
>> supported virtual generated columns in expression indexes. And then
>> prohibit SET EXPRESSION if the column is used in an index. Then you
>> don't need to worry about index rebuilding, ALTER TABLE recursion, new
>> catalog columns, and all that.
>>
>
>CREATE TABLE gtest22c (a int, b int GENERATED ALWAYS AS (a) VIRTUAL);
>CREATE INDEX gtest22c_a_idx ON gtest22c (a);
>CREATE INDEX gtest22c_b_idx ON gtest22c (b);
>
>If we don't add a new catalog column (just a single boolean
>indisvirtual is not enough, i think),
>how can we distinguish between the gtest22c_a_idx and gtest22c_b_idx
>indexes in the example above?
>
>If CREATE INDEX simply expands the virtual generated column expression
>without dependency tracking, that would be quite easy, see the
>attached v8.
>If so, we need to explicitly document that SET EXPRESSION has no
>effect on existing indexes that originally referenced the virtual
>generated column when CREATE INDEX was used.
>
>> But there is a comment in DefineIndex():
>>
>> /*
>> * XXX Virtual generated columns in index expressions or predicates
>> * could be supported, but it needs support in
>> * RelationGetIndexExpressions() and RelationGetIndexPredicate().
>> */
>>
>> which you delete, but you don't make any changes to those mentioned
>> functions. Maybe the comment is wrong, in which case, let's discuss
>> that and fix it. (If the comment is indeed wrong, then the feature
>> might even be very easy.)
>>
>
>I don't think it's a good idea to store the virtual generated columns
>as is in Form_pg_index->indkey
>because IndexInfo->ii_IndexAttrNumbers and Form->pg_index->indkey are
>referenced in too many places (see BuildIndexInfo).
>For every single occurrence of ndkey.values[i], we need to consider
>whether it's ok for it be a virtual generated column.
>Instead, Anum_pg_index_indexprs and Anum_pg_index_indpred store the
>expressions after the virtual generated columns expansion,
>then we don't need to worry about Form_pg_index->indkey.values[i] is
>virtual generated column or not.
>
>Therefore, i think RelationGetIndexExpressions and
>RelationGetIndexPredicate don't need to
>deal with virtual generated column expressions at all.
>Overall, I think the comment above is wrong.
>
>
>
>--
>jian
>https://www.enterprisedb.com/
Hi, hackers
Quick glossary:
VGC = virtual generated columns
SGC = stored generated columns
### 1. Iteration of implementation attempts
I first drafted V0: no VGC expansion during index creation, only temporary expansion at runtime.
The scope of required changes kept expanding (see `v0.patch.txt`), so I abandoned this approach.
Next I tried V1, expanding VGCs directly on index creation to support VGC in index expressions,
`INCLUDE` columns and partial index `WHERE` clauses. Unfortunately this would break metadata
catalog immutability. Before finalizing the patch (see `v1.patch.txt`), I found prior community
discussions and prototype attempts with several major design revisions:
https://postgr.es/m/CACJufxGao-cypdNhifHAdt8jHfK6-HX=tRBovBkgRuxw063GaA@mail.gmail.com
https://postgr.es/m/CACJufxGgkH0PyyqP6ggqcEWHxZzmkV=puY8ad=s8kisss9MAwg@mail.gmail.com
https://postgr.es/m/[email protected]
https://www.postgresql.org/message-id/flat/CACJufxExe7%2BGh5MKMFiN5xwwmPXaaZC1jjQOFuovv8Q7b1LgFw%40m...
https://www.postgresql.org/message-id/flat/CACJufxGgkH0PyyqP6ggqcEWHxZzmkV%3DpuY8ad%3Ds8kisss9MAwg%4...
https://commitfest.postgresql.org/patch/5667
https://commitfest.postgresql.org/patch/6094/
https://www.postgresql.org/message-id/flat/CAMbWs4811nG3w_3sLxps%2BEAuUsffA_83ZQ-1acEH_jQeq117mg%40m...
I then redesigned for V2. Developing this version made it clear how complex the feature is. Every change
was reviewed for local behavior plus upstream/downstream impacts, though some edge cases may still be overlooked.
### 2. Core design principles
1. **Goal**: Enable VGC support for regular index columns, expression columns, `INCLUDE` columns and columns used
in partial index predicates.
2. **Immutable metadata catalog**: We must not alter existing catalog entries(see v2-SQL_test_result.xlsx). VGCs are
benchmarked against SGCs to guarantee no regressions for index rebuild, logical replication, index comparison, stats
collection and `pg_dump`.
3. **Runtime expansion required**: The parser expands VGCs during query analysis, while SGCs are stored unexpanded
at index creation. Without runtime expansion, the planner cannot properly select access paths, sort strategies or partial indexes.
I added paired expand fields and helper functions to `IndexInfo`, `RelationData` and `IndexOptInfo`. Expanded expressions
are built once instead of dynamically on each access, hence paired code paths for raw and expanded fields.
4. I updated comments for `ComputeIndexAttrs()` and `makeIndexInfo()`. These functions remain unmodified; we expand
the new fields right after calling them for later access safety.
5. Major changes to `FormIndexDatum`: VGC values are computed on the fly via expanded expressions and stored inindex
tuples, behaving like SGCs to support index key evaluation, INCLUDE columns, expression indexes, and partial index matching.
### 3. Main challenge
The most tricky part is distinguishing which code paths should use raw non-expanded nodes versus expanded ones,
which consumed most of my development effort.
### 4. Code review scope
I audited all code paths for the following functions/fields to choose expansion mode and assess side effects:
makeIndexInfo|indpredExpand|indpred|indexprsExpand|indexprs|ii_PredicateState|ii_PredicateExpandState|
ii_PredicateExpand|ii_Predicate|ii_ExpressionsState|ii_ExpressionsExpandState|ii_ExpressionsExpand|ii_Expressions|
RelationGetIndexPredicateExpand |RelationGetIndexPredicate|RelationGetIndexExpressionsExpand|
RelationGetIndexExpressions|FormIndexDatum|ComputeIndexAttrs|rd_indpredExpand|rd_indpred|rd_indexprsExpand|rd_indexprs.
All changes and behavioral impacts are recorded in
v2-相关成员、函数的整体分析.xlsx (Chinese)
v2-Comprehensive Analysis of Related Members and Functions.xlsx (English).
### 5. Current status
While I have made substantial changes, full test coverage may still be incomplete. Community review, testing and
feedback are highly appreciated. Based on current progress and my testing (which is admittedly not comprehensive
or thorough), it aligns with SGCs in most metadata catalog aspects. see "v2-SQL_test_result.xlsx" including checking SQL.
Basic index scans now work, but there are still minor planner inconsistencies that I am debugging and resolving.
xman5=# \d+ t1
Table "public.t1"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+---------+-----------+----------+------------------------------------+---------+-------------+--------------+-------------
a | integer | | | | plain | | |
s1 | integer | | | generated always as (a * 1) stored | plain | | |
s2 | integer | | | generated always as (a * 2) stored | plain | | |
s3 | integer | | | generated always as (a * 3) stored | plain | | |
s4 | integer | | | generated always as (a * 4) stored | plain | | |
v1 | integer | | | generated always as (a * 11) | plain | | |
v2 | integer | | | generated always as (a * 22) | plain | | |
v3 | integer | | | generated always as (a * 33) | plain | | |
v4 | integer | | | generated always as (a * 44) | plain | | |
Indexes:
"idx_t1_s" btree (a, s1, abs(s2)) INCLUDE (s3) WHERE s4 < 100
"idx_t1_v" btree (a, v1, abs(v2)) INCLUDE (v3) WHERE v4 < 100
Access method: heap
Forexample:
xman5=# explain select a from t1 where s4 < 100;
QUERY PLAN
------------------------------------------------------------------------
Index Only Scan using idx_t1_s on t1 (cost=0.13..8.14 rows=1 width=4)
(1 row)
xman5=# explain select a from t1 where v4 < 100;
QUERY PLAN
-------------------------------------------------------------------------
Bitmap Heap Scan on t1 (cost=4.18..10.48 rows=153 width=4) -------still need heap Scan and Recheck Cond
Recheck Cond: ((a * 44) < 100)
-> Bitmap Index Scan on idx_t1_v (cost=0.00..4.14 rows=153 width=0)
(3 rows)
regards,
--
ZizhuanLiu (X-MAN)
[email protected]
Attachments:
[application/octet-stream] v0.patch.txt (12.5K, ../[email protected]/2-v0.patch.txt)
download
[application/octet-stream] v1.patch.txt (6.9K, ../[email protected]/3-v1.patch.txt)
download
[application/octet-stream] v2-相关成员、函数的整体分析.xlsx (47.4K, ../[email protected]/4-v2-%E7%9B%B8%E5%85%B3%E6%88%90%E5%91%98%E3%80%81%E5%87%BD%E6%95%B0%E7%9A%84%E6%95%B4%E4%BD%93%E5%88%86%E6%9E%90.xlsx)
download
[application/octet-stream] v2-Comprehensive Analysis of Related Members and Functions.xlsx (47.3K, ../[email protected]/5-v2-Comprehensive%20Analysis%20of%20Related%20Members%20and%20Functions.xlsx)
download
[application/octet-stream] v2-SQL_test_result.xlsx (27.7K, ../[email protected]/6-v2-SQL_test_result.xlsx)
download
[application/octet-stream] v2.pathch.txt (34.1K, ../[email protected]/7-v2.pathch.txt)
download
[application/octet-stream] v2.source_files.tar (1.8M, ../[email protected]/8-v2.source_files.tar)
download
[application/octet-stream] v2-0001-v2-enhance-index-support-for-virtual-generated-co.patch (35.3K, ../[email protected]/9-v2-0001-v2-enhance-index-support-for-virtual-generated-co.patch)
download | inline diff:
From 1098404a9b7f963b8e1c685f8166b76dd4c6cc82 Mon Sep 17 00:00:00 2001
From: Zizhuan Liu <[email protected]>
Date: Sun, 5 Jul 2026 10:30:49 +0800
Subject: [PATCH v2] v2 enhance-index-support-for-virtual-generated-columns
---
src/backend/access/heap/heapam_handler.c | 20 ++--
src/backend/bootstrap/bootstrap.c | 6 ++
src/backend/catalog/index.c | 61 +++++++++--
src/backend/catalog/indexing.c | 2 +
src/backend/catalog/toasting.c | 4 +
src/backend/commands/analyze.c | 10 +-
src/backend/commands/indexcmds.c | 42 +++-----
src/backend/executor/execIndexing.c | 13 ++-
src/backend/nodes/makefuncs.c | 10 +-
src/backend/optimizer/path/indxpath.c | 18 ++--
src/backend/optimizer/plan/createplan.c | 6 +-
src/backend/optimizer/util/plancat.c | 22 +++-
src/backend/utils/adt/selfuncs.c | 4 +-
src/backend/utils/cache/relcache.c | 126 +++++++++++++++++++++++
src/include/nodes/execnodes.h | 4 +
src/include/nodes/pathnodes.h | 2 +
src/include/utils/rel.h | 2 +
src/include/utils/relcache.h | 4 +
18 files changed, 286 insertions(+), 70 deletions(-)
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 2268cc27..22e0762e 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -1159,7 +1159,7 @@ heapam_index_build_range_scan(Relation heapRelation,
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
double reltuples;
- ExprState *predicate;
+ ExprState *predicateExpand;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@@ -1200,7 +1200,7 @@ heapam_index_build_range_scan(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
/*
* Prepare for scan of the base relation. In a normal index build, we use
@@ -1607,9 +1607,9 @@ heapam_index_build_range_scan(Relation heapRelation,
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
- if (predicate != NULL)
+ if (predicateExpand != NULL)
{
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
@@ -1709,7 +1709,9 @@ heapam_index_build_range_scan(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
+ indexInfo->ii_ExpressionsExpandState = NIL;
indexInfo->ii_PredicateState = NULL;
+ indexInfo->ii_PredicateExpandState = NULL;
return reltuples;
}
@@ -1726,7 +1728,7 @@ heapam_index_validate_scan(Relation heapRelation,
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
- ExprState *predicate;
+ ExprState *predicateExpand;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@@ -1758,7 +1760,7 @@ heapam_index_validate_scan(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
/*
* Prepare for scan of the base relation. We need just those tuples
@@ -1895,9 +1897,9 @@ heapam_index_validate_scan(Relation heapRelation,
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
- if (predicate != NULL)
+ if (predicateExpand != NULL)
{
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
@@ -1952,7 +1954,9 @@ heapam_index_validate_scan(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
+ indexInfo->ii_ExpressionsExpandState = NIL;
indexInfo->ii_PredicateState = NULL;
+ indexInfo->ii_PredicateExpandState = NULL;
}
/*
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b0dcd987..80bf4fc4 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1156,11 +1156,17 @@ index_register(Oid heap,
/* expressions will likely be null, but may as well copy it */
newind->il_info->ii_Expressions =
copyObject(indexInfo->ii_Expressions);
+ newind->il_info->ii_ExpressionsExpand =
+ copyObject(indexInfo->ii_ExpressionsExpand);
newind->il_info->ii_ExpressionsState = NIL;
+ newind->il_info->ii_ExpressionsExpandState = NIL;
/* predicate will likely be null, but may as well copy it */
newind->il_info->ii_Predicate =
copyObject(indexInfo->ii_Predicate);
+ newind->il_info->ii_PredicateExpand =
+ copyObject(indexInfo->ii_PredicateExpand);
newind->il_info->ii_PredicateState = NULL;
+ newind->il_info->ii_PredicateExpandState = NULL;
/* no exclusion constraints at bootstrap time, so no need to copy */
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 9407c357..ee260fde 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1403,6 +1403,10 @@ index_create_copy(Relation heapRelation, uint16 flags,
concurrently, /* concurrent */
indexRelation->rd_indam->amsummarizing,
oldInfo->ii_WithoutOverlaps);
+ newInfo->ii_ExpressionsExpand =
+ ExpandVirtualGeneratedColumns(newInfo->ii_ExpressionsExpand, heapRelation, InvalidOid);
+ newInfo->ii_PredicateExpand =
+ ExpandVirtualGeneratedColumns(newInfo->ii_PredicateExpand, heapRelation, InvalidOid);
/* fetch exclusion constraint info if any */
if (indexRelation->rd_index->indisexclusion)
@@ -2471,6 +2475,8 @@ BuildIndexInfo(Relation index)
false,
index->rd_indam->amsummarizing,
indexStruct->indisexclusion && indexStruct->indisunique);
+ ii->ii_ExpressionsExpand = (List *)RelationGetIndexExpressionsExpand(index);
+ ii->ii_PredicateExpand = (List *)RelationGetIndexPredicateExpand(index);
/* fill in attribute numbers */
for (i = 0; i < numAtts; i++)
@@ -2760,10 +2766,12 @@ FormIndexDatum(IndexInfo *indexInfo,
/* First time through, set up expression evaluation state */
indexInfo->ii_ExpressionsState =
ExecPrepareExprList(indexInfo->ii_Expressions, estate);
+ indexInfo->ii_ExpressionsExpandState =
+ ExecPrepareExprList(indexInfo->ii_ExpressionsExpand, estate);
/* Check caller has set up context correctly */
Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
}
- indexpr_item = list_head(indexInfo->ii_ExpressionsState);
+ indexpr_item = list_head(indexInfo->ii_ExpressionsExpandState);
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
@@ -2775,11 +2783,40 @@ FormIndexDatum(IndexInfo *indexInfo,
iDatum = slot_getsysattr(slot, keycol, &isNull);
else if (keycol != 0)
{
- /*
- * Plain index column; get the value we need directly from the
- * heap tuple.
- */
- iDatum = slot_getattr(slot, keycol, &isNull);
+ TupleDesc tupdesc = slot->tts_tupleDescriptor;
+ Form_pg_attribute att = TupleDescAttr(tupdesc, keycol - 1);
+
+ if (att->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
+ {
+ /*
+ * Plain index column; get the value we need directly from the
+ * heap tuple.
+ */
+ iDatum = slot_getattr(slot, keycol, &isNull);
+ }
+ else
+ {
+ TupleConstr *constr = tupdesc->constr;
+
+ for (int j = 0; j < constr->num_defval; j++)
+ {
+ AttrDefault *defval;
+ Expr *expr = NULL;
+ ExprState *exprstate;
+
+ defval = &constr->defval[j];
+ if (defval->adnum == keycol)
+ {
+ expr = stringToNode(defval->adbin);
+ exprstate = ExecPrepareExpr(expr, estate);
+ iDatum = ExecEvalExprSwitchContext(exprstate,
+ GetPerTupleExprContext(estate),
+ &isNull);
+ break;
+ }
+ Assert(j + 1 < constr->num_defval);
+ }
+ }
}
else
{
@@ -2791,7 +2828,7 @@ FormIndexDatum(IndexInfo *indexInfo,
iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item),
GetPerTupleExprContext(estate),
&isNull);
- indexpr_item = lnext(indexInfo->ii_ExpressionsState, indexpr_item);
+ indexpr_item = lnext(indexInfo->ii_ExpressionsExpandState, indexpr_item);
}
values[i] = iDatum;
isnull[i] = isNull;
@@ -3220,7 +3257,7 @@ IndexCheckExclusion(Relation heapRelation,
TableScanDesc scan;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
- ExprState *predicate;
+ ExprState *predicateExpand;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@@ -3246,7 +3283,7 @@ IndexCheckExclusion(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
/*
* Scan all live tuples in the base relation.
@@ -3266,9 +3303,9 @@ IndexCheckExclusion(Relation heapRelation,
/*
* In a partial index, ignore tuples that don't satisfy the predicate.
*/
- if (predicate != NULL)
+ if (predicateExpand != NULL)
{
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
@@ -3301,7 +3338,9 @@ IndexCheckExclusion(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
+ indexInfo->ii_ExpressionsExpandState = NIL;
indexInfo->ii_PredicateState = NULL;
+ indexInfo->ii_PredicateExpandState = NULL;
}
/*
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index fd7d2ec0..d27aaa09 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -133,7 +133,9 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple,
* supported, nor exclusion constraints, nor deferred uniqueness
*/
Assert(indexInfo->ii_Expressions == NIL);
+ Assert(indexInfo->ii_ExpressionsExpand == NIL);
Assert(indexInfo->ii_Predicate == NIL);
+ Assert(indexInfo->ii_PredicateExpand == NIL);
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(index->rd_index->indimmediate);
Assert(indexInfo->ii_NumIndexKeyAttrs != 0);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 4aa52a4b..daebfda1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -298,9 +298,13 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_IndexAttrNumbers[0] = 1;
indexInfo->ii_IndexAttrNumbers[1] = 2;
indexInfo->ii_Expressions = NIL;
+ indexInfo->ii_ExpressionsExpand = NIL;
indexInfo->ii_ExpressionsState = NIL;
+ indexInfo->ii_ExpressionsExpandState = NIL;
indexInfo->ii_Predicate = NIL;
+ indexInfo->ii_PredicateExpand = NIL;
indexInfo->ii_PredicateState = NULL;
+ indexInfo->ii_PredicateExpandState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index f66e80b7..fc67c24a 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -899,7 +899,7 @@ compute_index_stats(Relation onerel, double totalrows,
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
- ExprState *predicate;
+ ExprState *predicateExpand;
Datum *exprvals;
bool *exprnulls;
int numindexrows,
@@ -908,7 +908,7 @@ compute_index_stats(Relation onerel, double totalrows,
double totalindexrows;
/* Ignore index if no columns to analyze and not partial */
- if (attr_cnt == 0 && indexInfo->ii_Predicate == NIL)
+ if (attr_cnt == 0 && indexInfo->ii_PredicateExpand == NIL)
continue;
/*
@@ -926,7 +926,7 @@ compute_index_stats(Relation onerel, double totalrows,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
- predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
/* Compute and save index expression values */
exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -949,9 +949,9 @@ compute_index_stats(Relation onerel, double totalrows,
ExecStoreHeapTuple(heapTuple, slot, false);
/* If index is partial, check predicate */
- if (predicate != NULL)
+ if (predicateExpand != NULL)
{
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 9ab74c8d..8a63efc8 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -932,6 +932,8 @@ DefineIndex(ParseState *pstate,
concurrent,
amissummarizing,
stmt->iswithoutoverlaps);
+ indexInfo->ii_PredicateExpand =
+ ExpandVirtualGeneratedColumns(indexInfo->ii_PredicateExpand, rel, InvalidOid);
typeIds = palloc_array(Oid, numberOfAttributes);
collationIds = palloc_array(Oid, numberOfAttributes);
@@ -948,6 +950,8 @@ DefineIndex(ParseState *pstate,
root_save_userid, root_save_sec_context,
&root_save_nestlevel);
+ indexInfo->ii_ExpressionsExpand =
+ ExpandVirtualGeneratedColumns(indexInfo->ii_ExpressionsExpand, rel, InvalidOid);
/*
* Extra checks when creating a PRIMARY KEY index.
*/
@@ -1110,7 +1114,7 @@ DefineIndex(ParseState *pstate,
* We disallow indexes on system columns. They would not necessarily get
* updated correctly, and they don't seem useful anyway.
*
- * Also disallow virtual generated columns in indexes (use expression
+ * Aisallow virtual generated columns in indexes (include using expression
* index instead).
*/
for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
@@ -1123,7 +1127,7 @@ DefineIndex(ParseState *pstate,
errmsg("index creation on system columns is not supported")));
- if (attno > 0 &&
+ /*if (attno > 0 && i < numberOfKeyAttributes &&
TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -1131,17 +1135,15 @@ DefineIndex(ParseState *pstate,
errmsg("primary keys on virtual generated columns are not supported") :
stmt->isconstraint ?
errmsg("unique constraints on virtual generated columns are not supported") :
- errmsg("indexes on virtual generated columns are not supported"));
+ errmsg("indexes on virtual generated columns are not supported"));*/
}
/*
- * Also check for system and generated columns used in expressions or
- * predicates.
+ * Also check for system columns used in expressions or predicates.
*/
if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
{
Bitmapset *indexattrs = NULL;
- int j;
pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);
@@ -1154,25 +1156,6 @@ DefineIndex(ParseState *pstate,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("index creation on system columns is not supported")));
}
-
- /*
- * XXX Virtual generated columns in index expressions or predicates
- * could be supported, but it needs support in
- * RelationGetIndexExpressions() and RelationGetIndexPredicate().
- */
- j = -1;
- while ((j = bms_next_member(indexattrs, j)) >= 0)
- {
- AttrNumber attno = j + FirstLowInvalidHeapAttributeNumber;
-
- if (attno > 0 &&
- TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- stmt->isconstraint ?
- errmsg("unique constraints on virtual generated columns are not supported") :
- errmsg("indexes on virtual generated columns are not supported")));
- }
}
/* Is index safe for others to ignore? See set_indexsafe_procflags() */
@@ -1876,6 +1859,11 @@ CheckPredicate(Expr *predicate)
* If the caller switched to the table owner, ddl_userid is the role for ACL
* checks reached without traversing opaque expressions. Otherwise, it's
* InvalidOid, and other ddl_* arguments are undefined.
+ *
+ * Upon returning from this function, callers must apply
+ * ExpandVirtualGeneratedColumns() to ii_ExpressionsExpand
+ * when necessary for actual expansion if ii_ExpressionsExpand
+ * is not NIL or dummy.
*/
static void
ComputeIndexAttrs(ParseState *pstate,
@@ -2274,6 +2262,10 @@ ComputeIndexAttrs(ParseState *pstate,
attn++;
}
+
+ indexInfo->ii_ExpressionsExpand =
+ ExpandVirtualGeneratedColumns(copyObject(indexInfo->ii_Expressions),
+ NULL, relId);
}
/*
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index eb383812..b47daaf5 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -380,20 +380,24 @@ ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
if (indexInfo->ii_Predicate != NIL)
{
ExprState *predicate;
+ ExprState *predicateExpand;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
+ predicateExpand = indexInfo->ii_PredicateExpandState;
if (predicate == NULL)
{
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
indexInfo->ii_PredicateState = predicate;
+ indexInfo->ii_PredicateExpandState = predicateExpand;
}
/* Skip this index-update if the predicate isn't satisfied */
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
@@ -616,20 +620,24 @@ ExecCheckIndexConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
if (indexInfo->ii_Predicate != NIL)
{
ExprState *predicate;
+ ExprState *predicateExpand;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
+ predicateExpand = indexInfo->ii_PredicateExpandState;
if (predicate == NULL)
{
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
+ predicateExpand = ExecPrepareQual(indexInfo->ii_PredicateExpand, estate);
indexInfo->ii_PredicateState = predicate;
+ indexInfo->ii_PredicateExpandState = predicateExpand;
}
/* Skip this index-update if the predicate isn't satisfied */
- if (!ExecQual(predicate, econtext))
+ if (!ExecQual(predicateExpand, econtext))
continue;
}
@@ -1102,6 +1110,7 @@ index_unchanged_by_update(ResultRelInfo *resultRelInfo, EState *estate,
* pass hint.
*/
idxExprs = RelationGetIndexExpressions(indexRelation);
+ idxExprs = list_concat(idxExprs, RelationGetIndexExpressionsExpand(indexRelation));
hasexpression = index_expression_changed_walker((Node *) idxExprs,
allUpdatedCols);
list_free(idxExprs);
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 40b09958..946891b9 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -828,7 +828,11 @@ make_ands_implicit(Expr *clause)
/*
* makeIndexInfo
- * create an IndexInfo node
+ * create an IndexInfo node. Upon returning from this function,
+ * callers must apply ExpandVirtualGeneratedColumns()
+ * or RelationGetIndexExpressionsExpand or RelationGetIndexPredicateExpand() to
+ * ii_ExpressionsExpand and ii_PredicateExpand as needed for actual
+ * expansion when they are not NIL or dummy.
*/
IndexInfo *
makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
@@ -856,11 +860,15 @@ makeIndexInfo(int numattrs, int numkeyattrs, Oid amoid, List *expressions,
/* expressions */
n->ii_Expressions = expressions;
+ n->ii_ExpressionsExpand = copyObject(expressions);
n->ii_ExpressionsState = NIL;
+ n->ii_ExpressionsExpandState = NIL;
/* predicates */
n->ii_Predicate = predicates;
+ n->ii_PredicateExpand = copyObject(predicates);
n->ii_PredicateState = NULL;
+ n->ii_PredicateExpandState = NULL;
/* exclusion constraints */
n->ii_ExclusionOps = NULL;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 3f5d4fa3..b716ee92 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -1119,7 +1119,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
* just scanning the predOK index alone, no OR.)
*/
useful_predicate = false;
- if (index->indpred != NIL)
+ if (index->indpredExpand != NIL)
{
if (index->predOK)
{
@@ -1131,10 +1131,10 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
if (all_clauses == NIL)
all_clauses = list_concat_copy(clauses, other_clauses);
- if (!predicate_implied_by(index->indpred, all_clauses, false))
+ if (!predicate_implied_by(index->indpredExpand, all_clauses, false))
continue; /* can't use it at all */
- if (!predicate_implied_by(index->indpred, other_clauses, false))
+ if (!predicate_implied_by(index->indpredExpand, other_clauses, false))
useful_predicate = true;
}
}
@@ -2183,7 +2183,7 @@ find_indexpath_quals(Path *bitmapqual, List **quals, List **preds)
*quals = lappend(*quals, iclause->rinfo->clause);
}
- *preds = list_concat(*preds, ipath->indexinfo->indpred);
+ *preds = list_concat(*preds, ipath->indexinfo->indpredExpand);
}
else
elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
@@ -4038,11 +4038,11 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
ListCell *lcr;
- if (index->indpred == NIL)
+ if (index->indpredExpand == NIL)
continue; /* ignore non-partial indexes here */
if (!index->predOK) /* don't repeat work if already proven OK */
- index->predOK = predicate_implied_by(index->indpred, clauselist,
+ index->predOK = predicate_implied_by(index->indpredExpand, clauselist,
false);
/* If rel is an update target, leave indrestrictinfo as set above */
@@ -4068,7 +4068,7 @@ check_index_predicates(PlannerInfo *root, RelOptInfo *rel)
/* predicate_implied_by() assumes first arg is immutable */
if (contain_mutable_functions((Node *) rinfo->clause) ||
!predicate_implied_by(list_make1(rinfo->clause),
- index->indpred, false))
+ index->indpredExpand, false))
index->indrestrictinfo = lappend(index->indrestrictinfo, rinfo);
}
}
@@ -4402,14 +4402,14 @@ match_index_to_operand(Node *operand,
int i;
Node *indexkey;
- indexpr_item = list_head(index->indexprs);
+ indexpr_item = list_head(index->indexprsExpand);
for (i = 0; i < indexcol; i++)
{
if (index->indexkeys[i] == 0)
{
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
- indexpr_item = lnext(index->indexprs, indexpr_item);
+ indexpr_item = lnext(index->indexprsExpand, indexpr_item);
}
}
if (indexpr_item == NULL)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index de6a183d..2bdad7bd 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -3340,7 +3340,7 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
subindexECs = lappend(subindexECs, rinfo->parent_ec);
}
/* We can add any index predicate conditions, too */
- foreach(l, ipath->indexinfo->indpred)
+ foreach(l, ipath->indexinfo->indpredExpand)
{
Expr *pred = (Expr *) lfirst(l);
@@ -5142,7 +5142,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
}
/* It's an index expression, so find and cross-check the expression */
- indexpr_item = list_head(index->indexprs);
+ indexpr_item = list_head(index->indexprsExpand);
for (pos = 0; pos < index->ncolumns; pos++)
{
if (index->indexkeys[pos] == 0)
@@ -5167,7 +5167,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
else
elog(ERROR, "index key does not match expected index column");
}
- indexpr_item = lnext(index->indexprs, indexpr_item);
+ indexpr_item = lnext(index->indexprsExpand, indexpr_item);
}
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 7c4be174..72846d9e 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -433,24 +433,38 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* properly reduced.
*/
info->indexprs = RelationGetIndexExpressions(indexRelation);
+ info->indexprsExpand = RelationGetIndexExpressionsExpand(indexRelation);
info->indpred = RelationGetIndexPredicate(indexRelation);
+ info->indpredExpand = RelationGetIndexPredicateExpand(indexRelation);
if (info->indexprs)
{
if (varno != 1)
+ {
ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
+ ChangeVarNodes((Node *) info->indexprsExpand, 1, varno, 0);
+ }
info->indexprs = (List *)
eval_const_expressions(root, (Node *) info->indexprs);
+ info->indexprsExpand = (List *)
+ eval_const_expressions(root, (Node *) info->indexprsExpand);
}
if (info->indpred)
{
if (varno != 1)
+ {
ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
+ ChangeVarNodes((Node *) info->indpredExpand, 1, varno, 0);
+ }
info->indpred = (List *)
eval_const_expressions(root,
(Node *) make_ands_explicit(info->indpred));
+ info->indpredExpand = (List *)
+ eval_const_expressions(root,
+ (Node *) make_ands_explicit(info->indpredExpand));
info->indpred = make_ands_implicit((Expr *) info->indpred);
+ info->indpredExpand = make_ands_implicit((Expr *) info->indpredExpand);
}
/* Build targetlist using the completed indexprs data */
@@ -941,8 +955,8 @@ infer_arbiter_indexes(PlannerInfo *root)
attno - FirstLowInvalidHeapAttributeNumber);
}
- inferElems = RelationGetIndexExpressions(idxRel);
- inferIndexExprs = RelationGetIndexPredicate(idxRel);
+ inferElems = RelationGetIndexExpressionsExpand(idxRel);
+ inferIndexExprs = RelationGetIndexPredicateExpand(idxRel);
break;
}
}
@@ -1072,7 +1086,7 @@ infer_arbiter_indexes(PlannerInfo *root)
continue;
/* Expression attributes (if any) must match */
- idxExprs = RelationGetIndexExpressions(idxRel);
+ idxExprs = RelationGetIndexExpressionsExpand(idxRel);
if (idxExprs)
{
if (varno != 1)
@@ -1139,7 +1153,7 @@ infer_arbiter_indexes(PlannerInfo *root)
if (list_difference(idxExprs, inferElems) != NIL)
continue;
- predExprs = RelationGetIndexPredicate(idxRel);
+ predExprs = RelationGetIndexPredicateExpand(idxRel);
if (predExprs)
{
if (varno != 1)
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d6efd070..98e7216e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -7639,10 +7639,10 @@ add_predicate_to_index_quals(IndexOptInfo *index, List *indexQuals)
List *predExtraQuals = NIL;
ListCell *lc;
- if (index->indpred == NIL)
+ if (index->indpredExpand == NIL)
return indexQuals;
- foreach(lc, index->indpred)
+ foreach(lc, index->indpredExpand)
{
Node *predQual = (Node *) lfirst(lc);
List *oneQual = list_make1(predQual);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 0572ab42..e604bb91 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -91,6 +91,7 @@
#include "utils/resowner.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
+#include "rewrite/rewriteHandler.h"
#define RELCACHE_INIT_FILEMAGIC 0x573266 /* version ID value */
@@ -1583,7 +1584,9 @@ RelationInitIndexAccessInfo(Relation relation)
* expressions, predicate, exclusion caches will be filled later
*/
relation->rd_indexprs = NIL;
+ relation->rd_indexprsExpand = NIL;
relation->rd_indpred = NIL;
+ relation->rd_indpredExpand = NIL;
relation->rd_exclops = NULL;
relation->rd_exclprocs = NULL;
relation->rd_exclstrats = NULL;
@@ -5146,6 +5149,45 @@ RelationGetIndexExpressions(Relation relation)
return result;
}
+List *
+RelationGetIndexExpressionsExpand(Relation relation)
+{
+ List *result;
+ MemoryContext oldcxt;
+ bool isnull;
+ Datum heapRelidDatum;
+ Oid heapRelid;
+
+ /* Quick exit if we already computed the result. */
+ if (relation->rd_indexprsExpand)
+ return copyObject(relation->rd_indexprsExpand);
+
+ /* Quick exit if there is nothing to do. */
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indexprs, NULL))
+ return NIL;
+
+ result = RelationGetIndexExpressions(relation);
+ if (result == NIL)
+ return NIL;
+
+ heapRelidDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indrelid,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+ heapRelid = DatumGetObjectId(heapRelidDatum);
+
+ result = ExpandVirtualGeneratedColumns(result, NULL, heapRelid);
+
+ /* Now save a copy of the completed tree in the relcache entry. */
+ oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+ relation->rd_indexprsExpand = copyObject(result);
+ MemoryContextSwitchTo(oldcxt);
+
+ return result;
+}
+
/*
* RelationGetDummyIndexExpressions -- get dummy expressions for an index
*
@@ -5266,6 +5308,45 @@ RelationGetIndexPredicate(Relation relation)
return result;
}
+List *
+RelationGetIndexPredicateExpand(Relation relation)
+{
+ List *result;
+ MemoryContext oldcxt;
+ bool isnull;
+ Datum heapRelidDatum;
+ Oid heapRelid;
+
+ /* Quick exit if we already computed the result. */
+ if (relation->rd_indpredExpand)
+ return copyObject(relation->rd_indpredExpand);
+
+ /* Quick exit if there is nothing to do. */
+ if (relation->rd_indextuple == NULL ||
+ heap_attisnull(relation->rd_indextuple, Anum_pg_index_indpred, NULL))
+ return NIL;
+
+ result = RelationGetIndexPredicate(relation);
+ if (result == NIL)
+ return NIL;
+
+ heapRelidDatum = heap_getattr(relation->rd_indextuple,
+ Anum_pg_index_indrelid,
+ GetPgIndexDescriptor(),
+ &isnull);
+ Assert(!isnull);
+ heapRelid = DatumGetObjectId(heapRelidDatum);
+
+ result = ExpandVirtualGeneratedColumns(result, NULL, heapRelid);
+
+ /* Now save a copy of the completed tree in the relcache entry. */
+ oldcxt = MemoryContextSwitchTo(relation->rd_indexcxt);
+ relation->rd_indpredExpand = copyObject(result);
+ MemoryContextSwitchTo(oldcxt);
+
+ return result;
+}
+
/*
* RelationGetIndexAttrBitmap -- get a bitmap of index attribute numbers
*
@@ -6490,7 +6571,9 @@ load_relcache_init_file(bool shared)
rel->rd_partcheckvalid = false;
rel->rd_partcheckcxt = NULL;
rel->rd_indexprs = NIL;
+ rel->rd_indexprsExpand = NIL;
rel->rd_indpred = NIL;
+ rel->rd_indpredExpand = NIL;
rel->rd_exclops = NULL;
rel->rd_exclprocs = NULL;
rel->rd_exclstrats = NULL;
@@ -7019,3 +7102,46 @@ ResOwnerReleaseRelation(Datum res)
RelationCloseCleanup((Relation) DatumGetPointer(res));
}
+
+List *
+ExpandVirtualGeneratedColumns(List *list, Relation heapRelation, Oid heapRelId)
+{
+ bool opened_relation = false;
+ TupleDesc tupdesc;
+
+ if (list == NIL || (heapRelation == NULL && heapRelId == InvalidOid))
+ return list;
+
+ if (heapRelation == NULL)
+ {
+ heapRelation = table_open(heapRelId, NoLock);
+ opened_relation = true;
+ }
+
+ tupdesc = RelationGetDescr(heapRelation);
+ if ((tupdesc->constr && tupdesc->constr->has_generated_virtual))
+ {
+ int j;
+ Bitmapset *indexattrs = NULL;
+
+ pull_varattnos((Node *)list, 1, &indexattrs);
+
+ j = -1;
+ while ((j = bms_next_member(indexattrs, j)) >= 0)
+ {
+ AttrNumber attno = j + FirstLowInvalidHeapAttributeNumber;
+
+ if (attno > 0 &&
+ TupleDescAttr(tupdesc, attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
+ {
+ list = (List *)expand_generated_columns_in_expr((Node *)list, heapRelation, 1);
+ break;
+ }
+ }
+ }
+
+ if (opened_relation)
+ table_close(heapRelation, NoLock);
+
+ return list;
+}
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 53c13831..20a2e618 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -190,13 +190,17 @@ typedef struct IndexInfo
/* expr trees for expression entries, or NIL if none */
List *ii_Expressions; /* list of Expr */
+ List *ii_ExpressionsExpand; /* list of Expr */
/* exec state for expressions, or NIL if none */
List *ii_ExpressionsState; /* list of ExprState */
+ List *ii_ExpressionsExpandState; /* list of ExprState */
/* partial-index predicate, or NIL if none */
List *ii_Predicate; /* list of Expr */
+ List *ii_PredicateExpand; /* list of Expr */
/* exec state for expressions, or NIL if none */
ExprState *ii_PredicateState;
+ ExprState *ii_PredicateExpandState;
/* Per-column exclusion operators, or NULL if none */
Oid *ii_ExclusionOps; /* array with one entry per column */
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 27a2c681..9c65e123 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1403,8 +1403,10 @@ typedef struct IndexOptInfo
* print indextlist
*/
List *indexprs pg_node_attr(read_write_ignore);
+ List *indexprsExpand pg_node_attr(read_write_ignore);
/* predicate if a partial index, else NIL */
List *indpred;
+ List *indpredExpand;
/* targetlist representing index columns */
List *indextlist;
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index fa07ebf8..7bd01b36 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -210,7 +210,9 @@ typedef struct RelationData
struct FmgrInfo *rd_supportinfo; /* lookup info for support procedures */
int16 *rd_indoption; /* per-column AM-specific flags */
List *rd_indexprs; /* index expression trees, if any */
+ List *rd_indexprsExpand; /* expanded index expression trees, if any */
List *rd_indpred; /* index predicate tree, if any */
+ List *rd_indpredExpand; /* expanded index predicate tree, if any */
Oid *rd_exclops; /* OIDs of exclusion operators, if any */
Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */
uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 89c27aa1..70531bb8 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -58,8 +58,10 @@ extern List *RelationGetStatExtList(Relation relation);
extern Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok);
extern Oid RelationGetReplicaIndex(Relation relation);
extern List *RelationGetIndexExpressions(Relation relation);
+extern List *RelationGetIndexExpressionsExpand(Relation relation);
extern List *RelationGetDummyIndexExpressions(Relation relation);
extern List *RelationGetIndexPredicate(Relation relation);
+extern List *RelationGetIndexPredicateExpand(Relation relation);
extern bytea **RelationGetIndexAttOptions(Relation relation, bool copy);
/*
@@ -161,4 +163,6 @@ extern PGDLLIMPORT bool criticalRelcachesBuilt;
/* should be used only by relcache.c and postinit.c */
extern PGDLLIMPORT bool criticalSharedRelcachesBuilt;
+extern List *ExpandVirtualGeneratedColumns(List *list, Relation heapRelation, Oid heapRelId);
+
#endif /* RELCACHE_H */
--
2.43.0
view thread (10+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: support create index on virtual generated column.
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox