pg_orphaned扩展分析(二)

简介: pg_orphaned扩展是用于维护PostgreSQL孤儿文件的扩展,通过分析学习了查找孤儿文件的方法,同时还将学习在PostgreSQL后端(backend)如何查找指定表/视图、如何创建cache、如何使用hash表、如何使用List、如何使用正则表达式、C语言扩展如何返回结果集。

后端系统表扫描

pg_orphaned扩展通过在pg_class表中查找reltablespace和relfilenode以确定指定文件是否为孤儿文件,pg_orphaned是如何做呢?首先使用 InitDirtySnapshot 宏初始化一个类型为 SNAPSHOT_DIRTY SnapshotData结构,然后使用 open_table(>=pg12)或者tuple_open 打开表,最后使用 systable_beginscan 扫描,参考下面的代码片断:

SnapshotDataDirtySnapshot;
ScanKeyDataskey[2];
InitDirtySnapshot(DirtySnapshot);
......
#if PG_VERSION_NUM >= 120000relation=table_open(RelationRelationId, AccessShareLock);
#elserelation=heap_open(RelationRelationId, AccessShareLock);
#endif/* copy scankey to local copy, it will be modified during the scan */memcpy(skey, relfilenode_skey_dirty, sizeof(skey));
/* set scan arguments */skey[0].sk_argument=ObjectIdGetDatum(reltablespace);
skey[1].sk_argument=ObjectIdGetDatum(relfilenode);
scandesc=systable_beginscan(relation,
ClassTblspcRelfilenodeIndexId,
true,
&DirtySnapshot,
2,
skey);

SnapshotData 结构见 include/server/utils/snapshot.h。

open_table的实现在src/backend/access/table/table.c中,原型如下:

Relationtable_open(OidrelationId, LOCKMODElockmode)

参数relationId为要打开的表的oid,lockmode为加锁模式,定义如下:

/* NoLock is not a lock mode, but a flag value meaning "don't get a lock" */#define NoLock                  0#define AccessShareLock         1   /* SELECT */#define RowShareLock            2   /* SELECT FOR UPDATE/FOR SHARE */#define RowExclusiveLock        3   /* INSERT, UPDATE, DELETE */#define ShareUpdateExclusiveLock 4  /* VACUUM (non-FULL),ANALYZE, CREATE INDEX* CONCURRENTLY */#define ShareLock               5   /* CREATE INDEX (WITHOUT CONCURRENTLY) */#define ShareRowExclusiveLock   6   /* like EXCLUSIVE MODE, but allows ROW* SHARE */#define ExclusiveLock           7   /* blocks ROW SHARE/SELECT...FOR UPDATE */#define AccessExclusiveLock     8   /* ALTER TABLE, DROP TABLE, VACUUM FULL,* and unqualified LOCK TABLE */#define MaxLockMode             8   /* highest standard lock mode */

systable_beginscan 实现于src/backend/access/index/genam.c,用于对指定系统表进行heap-or-index扫描,共有六个参数,原型如下:

SysScanDescsystable_beginscan(RelationheapRelation,
OidindexId,
boolindexOK,
Snapshotsnapshot,
intnkeys, ScanKeykey)

共有六个参数:

heapRelation: 要扫描的oid,已经打开并加了合适的锁;

indexId: 查找条件的索引id;

indexOk: false强制heap scan;

snapshot: 为NULL时使用最近的catalog快照;

nkeys, key: 扫描使用的键值。

返回 SysScanDesc结构,定义如下:

/* Struct for storage-or-index scans of system tables */typedefstructSysScanDescData{
Relationheap_rel;       /* catalog being scanned */Relationirel;           /* NULL if doing heap scan */structTableScanDescData*scan; /* only valid in storage-scan case */structIndexScanDescData*iscan;    /* only valid in index-scan case */structSnapshotData*snapshot;  /* snapshot to unregister at end of scan */structTupleTableSlot*slot;
} SysScanDescData;

扫描后结果获取:

while (HeapTupleIsValid(ntp=systable_getnext(scandesc)))
        {
#if PG_VERSION_NUM >= 120000Form_pg_classclassform= (Form_pg_class) GETSTRUCT(ntp);
found=true;
Assert(classform->reltablespace==reltablespace);
Assert(classform->relfilenode==relfilenode);
relid=classform->oid;
#elsefound=true;
relid=HeapTupleGetOid(ntp);
#endif        }

结果后调用 systable_endscan 关闭扫描并释放资源,然后调用 close_table 关闭表。

systable_endscan(scandesc);
#if PG_VERSION_NUM >= 120000table_close(relation, AccessShareLock);
#elseheap_close(relation, AccessShareLock);
#endif

使用后端List

PostgreSQL后端实现了List包,位于src/backend/nodes/list.c。pg_orphaned扩展使用List保存孤独文件列表。

List*list_orphaned_relations=NULL;
typedefstructOrphanedRelation {
char*dbname;
char*path;
char*name;
intsize;
TimestampTzmod_time;
Oidrelfilenode;
Oidreloid;
} OrphanedRelation;

主要操作如下:

//新增oidrel=RelidByRelfilenodeDirty(reltablespace, relfilenode);
if (!OidIsValid(oidrel)) {
orph->dbname=strdup(dbname);
orph->path=strdup(dir);
orph->name=strdup(de->d_name);
orph->size= (int64) attrib.st_size;
orph->mod_time=time_t_to_timestamptz(attrib.st_mtime);
orph->relfilenode=relfilenode;
orph->reloid=oidrel;
*flist=lappend(*flist, orph);                             
}
......
//遍历#if (PG_VERSION_NUM < 130000)for (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(cell))
#elsefor (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(list_orphaned_relations, cell))
#endif    {
charorphaned_file[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
charorphaned_file_backup_dir[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
charorphaned_file_backup[MAXPGPATH+21+sizeof(TABLESPACE_VERSION_DIRECTORY) +10+6] = {0};
OrphanedRelation*orph= (OrphanedRelation*)lfirst(cell);

C语言自定义函数如何返回结果集

根据fmgr的定义,如果返回的是结果集而不是简单类型,需要使用指向ReturnSetInfo类型的节点的fcinfo->resultinfo调用该函数,该结构会由调用者初始化并传递给要调用的函数,参考代码如下(节选自pg_list_orphaned_internal):

ReturnSetInfo*rsinfo= (ReturnSetInfo*) fcinfo->resultinfo;
Tuplestorestate*tupstore;
TupleDesctupdesc;
MemoryContextper_query_ctx;
MemoryContextoldcontext;
ListCell*cell;
per_query_ctx=rsinfo->econtext->ecxt_per_query_memory;
oldcontext=MemoryContextSwitchTo(per_query_ctx);
/** 为我们的返回类型构造元组描述符*/if (get_call_result_type(fcinfo, NULL, &tupdesc) !=TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
/* 在work_mem中创建tuplestore */tupstore=tuplestore_begin_heap(true, false, work_mem);
rsinfo->returnMode=SFRM_Materialize;
rsinfo->setResult=tupstore;
rsinfo->setDesc=tupdesc;
MemoryContextSwitchTo(oldcontext);
for(...)
{
/* 获取数据 */OrphanedRelation*orph= (OrphanedRelation*)lfirst(cell);
/* 填充返回数据 */Datumvalues[8];
boolnulls[8];
memset(values, 0, sizeof(values));
memset(nulls, 0, sizeof(nulls));
values[0] =CStringGetTextDatum(orph->dbname);
values[1] =CStringGetTextDatum(orph->path);
values[2] =CStringGetTextDatum(orph->name);
values[3] =Int64GetDatum(orph->size);
values[4] =TimestampTzGetDatum(orph->mod_time);
values[5] =Int64GetDatum(orph->relfilenode);
values[6] =Int64GetDatum(orph->reloid);
if (orph->mod_time<=limitts)
values[7] =BoolGetDatum(true);
elsevalues[7] =BoolGetDatum(false);
/* 结果放入tuplestore中 */tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
/* 清理 */tuplestore_donestoring(tupstore);

正则表达式

正则表达式需要使用宽字符集,实现位于src/backend/utils/adt/regexp.c、/src/backend/regex/regcomp.c、/src/backend/regex/regexec.c,使用起来相对简单,见如下示例:

} elseif (de->d_name[0] =='t') {
inti;
pg_wchar*wstr;
intwlen;
pg_wchar*regwstr;
intregwlen;
intr;
char*regex="^t[0-9]*_[0-9]";
intregcomp_result;
charerrMsg[100];
regex_t*preg=palloc(sizeof(regex_t));
char*t;
char*tokptr=NULL;
char*temprel;
regwstr=palloc((strlen(regex) +1) *sizeof(pg_wchar));
regwlen=pg_mb2wchar_with_len(regex, regwstr, strlen(regex));
/* 编译正则表达式 */regcomp_result=pg_regcomp(preg,
regwstr,
regwlen,
REG_ADVANCED|REG_NOSUB,
DEFAULT_COLLATION_OID);
pfree(regwstr);
if (regcomp_result==REG_OKAY) {
wstr=palloc((strlen(de->d_name) +1) *sizeof(pg_wchar));
wlen=pg_mb2wchar_with_len(de->d_name, wstr, strlen(de->d_name));
/* 匹配正则表达式 */r=pg_regexec(preg, wstr, wlen, 0, NULL, 0, NULL, 0);
if (r!=REG_NOMATCH) {
            ......
相关文章
|
6月前
|
关系型数据库 C语言 PostgreSQL
pg_orphaned扩展分析(一)
pg_orphaned扩展是用于维护PostgreSQL孤儿文件的扩展,通过分析学习了查找孤儿文件的方法,同时还将学习在PostgreSQL后端(backend)如何查找指定表/视图、如何创建cache、如何使用hash表、如何使用List、如何使用正则表达式、C语言扩展如何返回结果集。
|
SQL 关系型数据库
[翻译]利用pg_stat_statments分析业务瓶颈
[翻译]利用pg_stat_statments分析业务瓶颈
107 0
|
SQL 监控 网络协议
优化PG查询:一问一答
优化PG查询:一问一答
126 0
|
6月前
|
存储 并行计算 关系型数据库
PolarDB 开源版通过pg_rational插件支持Stern-Brocot trees , 实现高效自定义顺序和调整顺序需求
背景PolarDB 的云原生存算分离架构, 具备低廉的数据存储、高效扩展弹性、高速多机并行计算能力、高速数据搜索和处理; PolarDB与计算算法结合, 将实现双剑合璧, 推动业务数据的价值产出, 将数据变成生产力.本文将介绍PolarDB 开源版通过pg_rational插件支持Stern-Bro...
95 0
|
存储 并行计算 Cloud Native
PolarDB 开源版通过pg_rational插件支持Stern-Brocot trees , 实现高效自定义顺序和调整顺序需求
PolarDB 的云原生存算分离架构, 具备低廉的数据存储、高效扩展弹性、高速多机并行计算能力、高速数据搜索和处理; PolarDB与计算算法结合, 将实现双剑合璧, 推动业务数据的价值产出, 将数据变成生产力. 本文将介绍PolarDB 开源版通过pg_rational插件支持Stern-Brocot trees , 实现高效自定义顺序和调整顺序需求.
176 0
|
存储 NoSQL 算法
DB 与 Elasticsearch 混合之应用系统场景分析探讨
从技术、业务两个层面探讨,为什么要使用 DB 结合 ES 混用的模式。
10429 1
DB 与 Elasticsearch 混合之应用系统场景分析探讨
|
SQL 存储 关系型数据库
【DB吐槽大会】第61期 - PG 审计功能有巨大增强空间
大家好,这里是DB吐槽大会,第61期 - PG 审计功能有巨大增强空间
|
关系型数据库 Shell 调度
|
关系型数据库 PostgreSQL 索引
|
SQL 关系型数据库 数据库