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) {
            ......
目录
相关文章
|
存储 SQL 监控
22 PostgreSQL 监控3PostgreSQL 性能快照和图形化分析工具 pg_stats_info 的使用|学习笔记
快速学习22 PostgreSQL 监控3PostgreSQL 性能快照和图形化分析工具 pg_stats_info 的使用
905 0
22 PostgreSQL 监控3PostgreSQL 性能快照和图形化分析工具 pg_stats_info 的使用|学习笔记
|
1月前
|
SQL 关系型数据库 数据库
一文熟悉PolarDB-PG 分区表核心特性
在 PolarDB-PG 数据库中,分区表 (Partitioned Table) 使您能够将非常大的表分解为更小且更易于管理的部分,这个部分称为分区 (Partition) 。 每个分区都是一个独立的对象,具有自己的名称和可选的存储特性。本文首先简单的介绍了分区表策略以及它的优势特点,然后介绍了PolarDB-PG 分区表支持的查询优化特性,最后介绍了分区表上的本地索引和全局索引,从而帮助用户对PolarDB-PG 分区表有一个全面的了解。
|
4月前
|
关系型数据库 C语言 PostgreSQL
pg_orphaned扩展分析(一)
pg_orphaned扩展是用于维护PostgreSQL孤儿文件的扩展,通过分析学习了查找孤儿文件的方法,同时还将学习在PostgreSQL后端(backend)如何查找指定表/视图、如何创建cache、如何使用hash表、如何使用List、如何使用正则表达式、C语言扩展如何返回结果集。
71 0
|
9月前
|
SQL 监控 网络协议
优化PG查询:一问一答
优化PG查询:一问一答
96 0
|
Oracle 安全 关系型数据库
|
关系型数据库 数据库 PostgreSQL
PG技术大讲堂 - Part 4:PostgreSQL实例结构
PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUG PG技术大讲堂。
158 1
PG技术大讲堂 - Part 4:PostgreSQL实例结构
|
SQL 弹性计算 关系型数据库
PostgreSQL 12 preview - CTE 增强,支持用户语法层控制 materialized 优化
标签 PostgreSQL , CTE , materialized , not materialized , push down 背景 PostgreSQL with 语法,能跑非常复杂的SQL逻辑,包括递归,多语句物化计算等。 在12以前的版本中,WITH中的每一个CTE(common table express),都是直接进行物化的,也就是说外层的条件不会推到CTE(物化节点)里
834 0
|
SQL 存储 关系型数据库
【DB吐槽大会】第61期 - PG 审计功能有巨大增强空间
大家好,这里是DB吐槽大会,第61期 - PG 审计功能有巨大增强空间
|
关系型数据库 Shell 调度
|
SQL 时序数据库 Apache
时序时空数据库新增TSQL语言:通过SQL即可让监控分析更简单更高效
阿里时序时空数据库TSDB最新推出TSQL,支持标准SQL的语法和函数。用户使用熟悉的SQL,不仅仅查询更简单易用,用户还可以利用SQL强大的功能,实现更加复杂的计算分析。
2661 0