Skip to content

Commit af7598f

Browse files
author
Commitfest Bot
committed
[CF 5667] v6 - support create index on virtual generated column.
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5667 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxHT-1a-uP9pEq6rpuvq8mS0V0xOHr30R4THsDeo5DyTUA@mail.gmail.com Author(s): Jian He
2 parents 5c8eda1 + a051d28 commit af7598f

File tree

17 files changed

+916
-73
lines changed

17 files changed

+916
-73
lines changed

contrib/pageinspect/expected/btree.out

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ tids |
183183

184184
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
185185
ERROR: block number 2 is out of range for relation "test1_a_idx"
186+
---test index over virtual generated column
187+
CREATE TABLE test4(a int, b int GENERATED ALWAYS AS (a + 1), c text);
188+
INSERT INTO test4(a,c) VALUES (10,11), (10,11);
189+
CREATE INDEX test4_b_idx ON test4 USING btree (b);
190+
CREATE INDEX test4_a_1_idx ON test4 USING btree ((a+1));
191+
--expect return zero row
192+
SELECT * FROM bt_page_items('test4_b_idx', 1)
193+
EXCEPT ALL
194+
SELECT * FROM bt_page_items('test4_a_1_idx', 1);
195+
(0 rows)
196+
186197
-- Failure when using a non-btree index.
187198
CREATE INDEX test1_a_hash ON test1 USING hash(a);
188199
SELECT bt_metap('test1_a_hash');

contrib/pageinspect/sql/btree.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0));
3232
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1));
3333
SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2));
3434

35+
---test index over virtual generated column
36+
CREATE TABLE test4(a int, b int GENERATED ALWAYS AS (a + 1), c text);
37+
INSERT INTO test4(a,c) VALUES (10,11), (10,11);
38+
CREATE INDEX test4_b_idx ON test4 USING btree (b);
39+
CREATE INDEX test4_a_1_idx ON test4 USING btree ((a+1));
40+
--expect return zero row
41+
SELECT * FROM bt_page_items('test4_b_idx', 1)
42+
EXCEPT ALL
43+
SELECT * FROM bt_page_items('test4_a_1_idx', 1);
44+
3545
-- Failure when using a non-btree index.
3646
CREATE INDEX test1_a_hash ON test1 USING hash(a);
3747
SELECT bt_metap('test1_a_hash');

doc/src/sgml/catalogs.sgml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4589,6 +4589,21 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
45894589
</para></entry>
45904590
</row>
45914591

4592+
<row>
4593+
<entry role="catalog_table_entry"><para role="column_definition">
4594+
<structfield>indattrgenerated</structfield> <type>int2vector</type>
4595+
(references <link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.<structfield>attnum</structfield>)
4596+
</para>
4597+
<para>
4598+
This is an array of <structfield>indnatts</structfield> values that
4599+
indicate which virtual generated columns this index indexes.
4600+
For example, a value of <literal>1 3</literal> would mean that the first
4601+
and the third table columns of this index entries are virtual generated
4602+
column. A zero in this array indicates that the corresponding index
4603+
attribute is not virtual generated column reference.
4604+
</para></entry>
4605+
</row>
4606+
45924607
<row>
45934608
<entry role="catalog_table_entry"><para role="column_definition">
45944609
<structfield>indexprs</structfield> <type>pg_node_tree</type>

src/backend/catalog/index.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,12 @@ UpdateIndexRelation(Oid indexoid,
584584
Relation pg_index;
585585
HeapTuple tuple;
586586
int i;
587+
int2vector *indgenkey;
588+
int16 *colgenerated;
589+
590+
colgenerated = palloc_array(int16, indexInfo->ii_NumIndexAttrs);
591+
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
592+
colgenerated[i] = indexInfo->ii_IndexAttrGeneratedNumbers[i];
587593

588594
/*
589595
* Copy the index key, opclass, and indoption info into arrays (should we
@@ -595,6 +601,7 @@ UpdateIndexRelation(Oid indexoid,
595601
indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexKeyAttrs);
596602
indclass = buildoidvector(opclassOids, indexInfo->ii_NumIndexKeyAttrs);
597603
indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexKeyAttrs);
604+
indgenkey = buildint2vector(colgenerated, indexInfo->ii_NumIndexAttrs);
598605

599606
/*
600607
* Convert the index expressions (if any) to a text datum
@@ -653,6 +660,7 @@ UpdateIndexRelation(Oid indexoid,
653660
values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation);
654661
values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass);
655662
values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption);
663+
values[Anum_pg_index_indattrgenerated - 1] = PointerGetDatum(indgenkey);
656664
values[Anum_pg_index_indexprs - 1] = exprsDatum;
657665
if (exprsDatum == (Datum) 0)
658666
nulls[Anum_pg_index_indexprs - 1] = true;
@@ -1134,6 +1142,28 @@ index_create(Relation heapRelation,
11341142
}
11351143
}
11361144

1145+
/*
1146+
* Internally, we convert index of virtual generation column into an
1147+
* expression index. For example, if column 'b' is defined as (b INT
1148+
* GENERATED ALWAYS AS (a * 2) VIRTUAL) then index over 'b' would
1149+
* transformed into an expression index as ((a * 2)). As a result,
1150+
* the pg_depend refobjsubid does not retain the original attribute
1151+
* number of the virtual generated column. But we need rebuild any
1152+
* index that was build on virtual generated column. so we need auto
1153+
* dependencies on referenced virtual generated columns.
1154+
*/
1155+
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
1156+
{
1157+
if (indexInfo->ii_IndexAttrGeneratedNumbers[i] != 0)
1158+
{
1159+
ObjectAddressSubSet(referenced, RelationRelationId,
1160+
heapRelationId,
1161+
indexInfo->ii_IndexAttrGeneratedNumbers[i]);
1162+
add_exact_object_address(&referenced, addrs);
1163+
have_simple_col = false;
1164+
}
1165+
}
1166+
11371167
/*
11381168
* If there are no simply-referenced columns, give the index an
11391169
* auto dependency on the whole table. In most cases, this will
@@ -1411,6 +1441,7 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId,
14111441

14121442
indexColNames = lappend(indexColNames, NameStr(att->attname));
14131443
newInfo->ii_IndexAttrNumbers[i] = oldInfo->ii_IndexAttrNumbers[i];
1444+
newInfo->ii_IndexAttrGeneratedNumbers[i] = oldInfo->ii_IndexAttrGeneratedNumbers[i];
14141445
}
14151446

14161447
/* Extract opclass options for each attribute */
@@ -2428,9 +2459,12 @@ IndexInfo *
24282459
BuildIndexInfo(Relation index)
24292460
{
24302461
IndexInfo *ii;
2462+
HeapTuple ht_idx;
24312463
Form_pg_index indexStruct = index->rd_index;
24322464
int i;
24332465
int numAtts;
2466+
Datum indgenkeyDatum;
2467+
int2vector *indgenkey;
24342468

24352469
/* check the number of keys, and copy attr numbers into the IndexInfo */
24362470
numAtts = indexStruct->indnatts;
@@ -2454,9 +2488,19 @@ BuildIndexInfo(Relation index)
24542488
index->rd_indam->amsummarizing,
24552489
indexStruct->indisexclusion && indexStruct->indisunique);
24562490

2491+
ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexStruct->indexrelid));
2492+
indgenkeyDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
2493+
Anum_pg_index_indattrgenerated);
2494+
indgenkey = (int2vector *) DatumGetPointer(indgenkeyDatum);
2495+
24572496
/* fill in attribute numbers */
24582497
for (i = 0; i < numAtts; i++)
2498+
{
24592499
ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i];
2500+
ii->ii_IndexAttrGeneratedNumbers[i] = indgenkey->values[i];
2501+
}
2502+
2503+
ReleaseSysCache(ht_idx);
24602504

24612505
/* fetch exclusion constraint info if any */
24622506
if (indexStruct->indisexclusion)
@@ -2518,11 +2562,35 @@ BuildDummyIndexInfo(Relation index)
25182562
for (i = 0; i < numAtts; i++)
25192563
ii->ii_IndexAttrNumbers[i] = indexStruct->indkey.values[i];
25202564

2565+
/*
2566+
* Index expressions or predicates are skipped here, see above comments. If
2567+
* virtual generated columns references another column, ii_IndexAttrNumbers
2568+
* will set to that referenced column. So do nothing for
2569+
* ii_IndexAttrGeneratedNumbers here.
2570+
*/
2571+
25212572
/* We ignore the exclusion constraint if any */
25222573

25232574
return ii;
25242575
}
25252576

2577+
/*
2578+
* IndexOverVirtualGenerated
2579+
* Return whether this index was built on virtual generated column.
2580+
*/
2581+
bool
2582+
IsIndexOverVirtualGenerated(const IndexInfo *info)
2583+
{
2584+
int i;
2585+
2586+
for (i = 0; i < info->ii_NumIndexAttrs; i++)
2587+
{
2588+
if (AttributeNumberIsValid(info->ii_IndexAttrGeneratedNumbers[i]))
2589+
return true;
2590+
}
2591+
return false;
2592+
}
2593+
25262594
/*
25272595
* CompareIndexInfo
25282596
* Return whether the properties of two indexes (in different tables)
@@ -2585,6 +2653,15 @@ CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2,
25852653
return false;
25862654
}
25872655

2656+
if (AttributeNumberIsValid(info1->ii_IndexAttrGeneratedNumbers[i]) ||
2657+
AttributeNumberIsValid(info2->ii_IndexAttrGeneratedNumbers[i]))
2658+
{
2659+
/* fail if index over virtual generated column does not match */
2660+
if (attmap->attnums[info2->ii_IndexAttrGeneratedNumbers[i] - 1] !=
2661+
info1->ii_IndexAttrGeneratedNumbers[i])
2662+
return false;
2663+
}
2664+
25882665
/* collation and opfamily are not valid for included columns */
25892666
if (i >= info1->ii_NumIndexKeyAttrs)
25902667
continue;

0 commit comments

Comments
 (0)