后端系统表扫描
pg_orphaned扩展通过在pg_class表中查找reltablespace和relfilenode以确定指定文件是否为孤儿文件,pg_orphaned是如何做呢?首先使用 InitDirtySnapshot 宏初始化一个类型为 SNAPSHOT_DIRTY 的 SnapshotData结构,然后使用 open_table(>=pg12)或者tuple_open 打开表,最后使用 systable_beginscan 扫描,参考下面的代码片断:
SnapshotDataDirtySnapshot; ScanKeyDataskey[2]; InitDirtySnapshot(DirtySnapshot); ...... relation=table_open(RelationRelationId, AccessShareLock); relation=heap_open(RelationRelationId, AccessShareLock); /* 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" *//* SELECT *//* SELECT FOR UPDATE/FOR SHARE *//* INSERT, UPDATE, DELETE *//* VACUUM (non-FULL),ANALYZE, CREATE INDEX* CONCURRENTLY *//* CREATE INDEX (WITHOUT CONCURRENTLY) *//* like EXCLUSIVE MODE, but allows ROW* SHARE *//* blocks ROW SHARE/SELECT...FOR UPDATE *//* ALTER TABLE, DROP TABLE, VACUUM FULL,* and unqualified LOCK TABLE *//* 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))) { Form_pg_classclassform= (Form_pg_class) GETSTRUCT(ntp); found=true; Assert(classform->reltablespace==reltablespace); Assert(classform->relfilenode==relfilenode); relid=classform->oid; found=true; relid=HeapTupleGetOid(ntp); }
结果后调用 systable_endscan 关闭扫描并释放资源,然后调用 close_table 关闭表。
systable_endscan(scandesc); table_close(relation, AccessShareLock); heap_close(relation, AccessShareLock);
使用后端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); } ...... //遍历for (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(cell)) for (cell=list_head(list_orphaned_relations); cell!=NULL; cell=lnext(list_orphaned_relations, cell)) { 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) { ......