Postgresql源码(52)bitmapset分析RelationGetIndexAttrBitmap

2022-05-31 10:04:59 浏览数 (1)

相关 《Postgresql源码(47)bitmapset原理》 《Postgresql源码(52)bitmapset分析RelationGetIndexAttrBitmap》

总结

RelationGetIndexAttrBitmap可以返回四个bitmapset标识所有索引的位置,位置全部加过偏移量(7)。

例如下面SQL有三个索引,最终返回的四个bitmapset见下面最后一行

代码语言:javascript复制
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值
  • rd_indexattr:所有索引位置,1101100000000记录了8、9、11、12,减去系统列偏移7,表示1、2、4、5列有索引。
  • rd_keyattr:唯一索引位置
  • rd_pkattr:主键索引位置
  • rd_idattr:复制标识位置,默认是主键

例子

  • 第一列a:主键
  • 第二列b:普通BTREE索引
  • 第三列c:无索引
  • 第四列d:唯一、联合索引
  • 第五列e:唯一、联合索引
代码语言:javascript复制
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;

RelationGetIndexAttrBitmap流程

attrKind = INDEX_ATTR_BITMAP_ALL

代码语言:javascript复制
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会填入值,后面可以直接使用

代码语言:javascript复制
	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

代码语言:javascript复制
(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会把主键和复制索引顺便算出来

代码语言:javascript复制
(gdb) p	relpkindex
$13 = 24762
(gdb) p	relreplindex
$14 = 24762

继续

代码语言:javascript复制
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中的配置

代码语言:javascript复制
		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

代码语言:javascript复制
		/* 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);
			}
		}
    ...
	}
...
}

对于三个索引的配置总结:

代码语言:javascript复制
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]

0 人点赞