相关 《Postgresql源码(47)bitmapset原理》 《Postgresql源码(52)bitmapset分析RelationGetIndexAttrBitmap》
RelationGetIndexAttrBitmap可以返回四个bitmapset标识所有索引的位置,位置全部加过偏移量(7)。
例如下面SQL有三个索引,最终返回的四个bitmapset见下面最后一行
create table t1(a int primary key, b int, c int, d int, e int);
create index idx_t1_b on t1(b);
create unique index idx_t1_d_e on t1(d,e);
attnum rd_indexattr rd_keyattr rd_pkattr rd_idattr
t1_pkey: 1 0000100000000 0000100000000 0100000000 0100000000
idx_t1_b: 2 0001100000000 0000100000000 0100000000 0100000000
idx_t1_d_e: 4 0101100000000 0100100000000 0100000000 0100000000
5 1101100000000 1100100000000 0100000000 0100000000 <---最后返回的四个bitmap值
drop table t1;
create table t1(a int primary key, b int, c int, d int, e int);
create index idx_t1_b on t1(b);
create unique index idx_t1_d_e on t1(d,e);
insert into t1 values (1, 100, 10, 1, 11);
insert into t1 values (2, 200, 20, 2, 22);
insert into t1 values (3, 300, 30, 3, 33);
insert into t1 values (4, 400, 40, 4, 44);
insert into t1 values (5, 500, 50, 5, 55);
-- 场景一:更新无索引列,查找项有索引
update t1 set c = 0 where a = 1;
attrKind = INDEX_ATTR_BITMAP_ALL
Bitmapset *
RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
{
Bitmapset *indexattrs; /* indexed columns */
Bitmapset *uindexattrs; /* columns in unique indexes */
Bitmapset *pkindexattrs; /* columns in the primary index */
Bitmapset *idindexattrs; /* columns in the replica identity */
List *indexoidlist;
List *newindexoidlist;
Oid relpkindex;
Oid relreplindex;
ListCell *l;
MemoryContext oldcxt;
第一次执行后rd_indexattr会填入值,后面可以直接使用
if (relation->rd_indexattr != NULL)
{
switch (attrKind)
{
case INDEX_ATTR_BITMAP_ALL:
return bms_copy(relation->rd_indexattr);
case INDEX_ATTR_BITMAP_KEY:
return bms_copy(relation->rd_keyattr);
case INDEX_ATTR_BITMAP_PRIMARY_KEY:
return bms_copy(relation->rd_pkattr);
case INDEX_ATTR_BITMAP_IDENTITY_KEY:
return bms_copy(relation->rd_idattr);
default:
elog(ERROR, "unknown attrKind %u", attrKind);
}
}
...
RelationGetIndexList查到三个索引的OID,用表的OID查出来的indrelid = 24759
(gdb) p indexoidlist->elements[0]->oid_value
$10 = 24762
(gdb) p indexoidlist->elements[1]->oid_value
$11 = 24764
(gdb) p indexoidlist->elements[2]->oid_value
$12 = 24765
postgres=# select indexrelid,indisprimary,indisunique,indkey from pg_index where indrelid = 24759;
indexrelid | indisprimary | indisunique | indkey
------------+--------------+-------------+--------
24762 | t | t | 1
24764 | f | f | 2
24765 | f | t | 4 5
RelationGetIndexList会把主键和复制索引顺便算出来
(gdb) p relpkindex
$13 = 24762
(gdb) p relreplindex
$14 = 24762
继续
restart:
indexoidlist = RelationGetIndexList(relation);
...
relpkindex = relation->rd_pkindex;
relreplindex = relation->rd_replidindex;
/*
* For each index, add referenced attributes to indexattrs.
*
* Note: we consider all indexes returned by RelationGetIndexList, even if
* they are not indisready or indisvalid. This is important because an
* index for which CREATE INDEX CONCURRENTLY has just started must be
* included in HOT-safety decisions (see README.HOT). If a DROP INDEX
* CONCURRENTLY is far enough along that we should ignore the index, it
* won't be returned at all by RelationGetIndexList.
*/
indexattrs = NULL;
uindexattrs = NULL;
pkindexattrs = NULL;
idindexattrs = NULL;
foreach(l, indexoidlist)
{
Oid indexOid = lfirst_oid(l);
Relation indexDesc;
Datum datum;
bool isnull;
Node *indexExpressions;
Node *indexPredicate;
int i;
bool isKey; /* candidate key */
bool isPK; /* primary key */
bool isIDKey; /* replica identity index */
检查index tuple中的配置
indexDesc = index_open(indexOid, AccessShareLock);
datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indexprs,
GetPgIndexDescriptor(), &isnull);
if (!isnull)
indexExpressions = stringToNode(TextDatumGetCString(datum));
else
indexExpressions = NULL;
datum = heap_getattr(indexDesc->rd_indextuple, Anum_pg_index_indpred,
GetPgIndexDescriptor(), &isnull);
if (!isnull)
indexPredicate = stringToNode(TextDatumGetCString(datum));
else
indexPredicate = NULL;
/* Can this index be referenced by a foreign key? */
isKey = indexDesc->rd_index->indisunique &&
indexExpressions == NULL &&
indexPredicate == NULL;
/* Is this a primary key? */
isPK = (indexOid == relpkindex);
/* Is this index the configured (or default) replica identity? */
isIDKey = (indexOid == relreplindex);
开始配置bitmap,三个索引每个所以都会进入下面for。
注意:配置bms_add_member的时候为什么要减去FirstLowInvalidHeapAttributeNumber?
原因:每一行的列都有6个前置系统列,所以真实的列位置需要+7
/* Collect simple attribute references */
for (i = 0; i < indexDesc->rd_index->indnatts; i++)
{
int attrnum = indexDesc->rd_index->indkey.values[i];
if (attrnum != 0)
{
indexattrs = bms_add_member(indexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
if (isKey && i < indexDesc->rd_index->indnkeyatts)
uindexattrs = bms_add_member(uindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
if (isPK && i < indexDesc->rd_index->indnkeyatts)
pkindexattrs = bms_add_member(pkindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
if (isIDKey && i < indexDesc->rd_index->indnkeyatts)
idindexattrs = bms_add_member(idindexattrs,
attrnum - FirstLowInvalidHeapAttributeNumber);
}
}
...
}
...
}
对于三个索引的配置总结:
create table t1(a int primary key, b int, c int, d int, e int);
create index idx_t1_b on t1(b);
create unique index idx_t1_d_e on t1(d,e);
all --> indexattrs --> relation->rd_indexattr
isKey --> uindexattrs --> relation->rd_keyattr // Can this index be referenced by a foreign key?
isPK --> pkindexattrs --> relation->rd_pkattr // Is this a primary key
isIDKey --> idindexattrs --> relation->rd_idattr // Is this index the configured (or default) replica identity
attnum rd_indexattr rd_keyattr rd_pkattr rd_idattr
t1_pkey: 1 0000100000000 0000100000000 0100000000 0100000000
idx_t1_b: 2 0001100000000 0000100000000 0100000000 0100000000
idx_t1_d_e: 4 0101100000000 0100100000000 0100000000 0100000000
5 1101100000000 1100100000000 0100000000 0100000000
-- gdb
p/t indexattrs->words[0]
p/t uindexattrs->words[0]
p/t pkindexattrs->words[0]
p/t idindexattrs->words[0]