分析PostgreSQL创建函数的过程
本篇内容主要讲解“分析PostgreSQL创建函数的过程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析PostgreSQL创建函数的过程”吧!
一、数据结构
Form_pg_language
plpgsql语言定义结构体
CATALOG(pg_language,2612,LanguageRelationId)
{
Oid oid;
NameData lanname;
Oid lanowner BKI_DEFAULT(PGUID);
bool lanispl BKI_DEFAULT(f);
bool lanpltrusted BKI_DEFAULT(f);
Oid lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
Oid laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
Oid lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc);
#ifdef CATALOG_VARLEN
aclitem lanacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_language;
typedef FormData_pg_language *Form_pg_language;
ArrayType
typedef struct
{
//可变的header
int32 vl_len_;
//维度
int ndim;
//指向数据的偏移量,如为0则表示没有位图
int32 dataoffset;
//元素类型的OID
Oid elemtype;
} ArrayType;
DefElem
typedef struct DefElem
{
NodeTag type;
char *defnamespace;
char *defname;
Node *arg;
DefElemAction defaction;
int location;
} DefElem;
FunctionParameter
typedef enum FunctionParameterMode
{
FUNC_PARAM_IN = 'i',
FUNC_PARAM_OUT = 'o',
FUNC_PARAM_INOUT = 'b',
FUNC_PARAM_VARIADIC = 'v',
FUNC_PARAM_TABLE = 't'
} FunctionParameterMode;
typedef struct FunctionParameter
{
NodeTag type;
char *name;
TypeName *argType;
FunctionParameterMode mode;
Node *defexpr;
} FunctionParameter;
CatCache
typedef uint32 (*CCHashFN) (Datum datum);
typedef bool (*CCFastEqualFN) (Datum a, Datum b);
typedef struct catcache
{
//cache ID
int id;
//cache的hash槽
int cc_nbuckets;
//元组描述符
TupleDesc cc_tupdesc;
//hash桶
dlist_head *cc_bucket;
//每个key的hash函数
CCHashFN cc_hashfunc[CATCACHE_MAXKEYS];
//每个key的快速等值函数
CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS];
//每个key的属性编号
int cc_keyno[CATCACHE_MAXKEYS];
//CatCList结构体链表
dlist_head cc_lists;
//cache中元组数
int cc_ntup;
//keys数
int cc_nkeys;
//cache元组相关的relation
const char *cc_relname;
//relation OID
Oid cc_reloid;
//匹配缓存keys的索引OID
Oid cc_indexoid;
//是否可跨库共享?
bool cc_relisshared;
//链表链接
slist_node cc_next;
//用于heap扫描的预计算key信息
ScanKeyData cc_skey[CATCACHE_MAXKEYS];
#ifdef CATCACHE_STATS
//检索次数
long cc_searches;
//匹配次数
long cc_hits;
//未命中次数
long cc_neg_hits;
//未命中成功加载次数
long cc_newloads;
//验证失效次数
long cc_invals;
//链表检索次数
long cc_lsearches;
//
long cc_lhits;
#endif
} CatCache;
二、源码解读
HeapTuple
SearchSysCache3(int cacheId,
Datum key1, Datum key2, Datum key3)
{
//执行检查
Assert(cacheId >= 0 && cacheId < SysCacheSize &&
PointerIsValid(SysCache[cacheId]));
Assert(SysCache[cacheId]->cc_nkeys == 3);
//直接调用SearchCatCache3
return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
}
HeapTuple
SearchCatCache3(CatCache *cache,
Datum v1, Datum v2, Datum v3)
{
//直接调用SearchCatCacheInternal
return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
}
static inline HeapTuple
SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1,
Datum v2,
Datum v3,
Datum v4)
{
//#define CATCACHE_MAXKEYS 4
Datum arguments[CATCACHE_MAXKEYS];
uint32 hashValue;
Index hashIndex;
dlist_iter iter;
dlist_head *bucket;
CatCTup *ct;
//确保处于事务中
Assert(IsTransactionState());
Assert(cache->cc_nkeys == nkeys);
if (unlikely(cache->cc_tupdesc == NULL))
CatalogCacheInitializeCache(cache);
#ifdef CATCACHE_STATS
cache->cc_searches++;
#endif
//初始化本地参数数组
arguments[0] = v1;
arguments[1] = v2;
arguments[2] = v3;
arguments[3] = v4;
hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
bucket = &cache->cc_bucket[hashIndex];
dlist_foreach(iter, bucket)
{
ct = dlist_container(CatCTup, cache_elem, iter.cur);
if (ct->dead)//忽略已废弃的条目
continue;
if (ct->hash_value != hashValue)//跳过hash不同的项
continue;
//不同的元组,跳过
if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
continue;
dlist_move_head(bucket, &ct->cache_elem);
if (!ct->negative)
{
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
ct->refcount++;
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
cache->cc_relname, hashIndex);
#ifdef CATCACHE_STATS
cache->cc_hits++;
#endif
return &ct->tuple;
}
else
{
CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
cache->cc_relname, hashIndex);
#ifdef CATCACHE_STATS
cache->cc_neg_hits++;
#endif
return NULL;
}
}
return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
}
static pg_noinline HeapTuple
SearchCatCacheMiss(CatCache *cache,
int nkeys,
uint32 hashValue,
Index hashIndex,
Datum v1,
Datum v2,
Datum v3,
Datum v4)
{
ScanKeyData cur_skey[CATCACHE_MAXKEYS];
Relation relation;
SysScanDesc scandesc;
HeapTuple ntp;
CatCTup *ct;
Datum arguments[CATCACHE_MAXKEYS];
arguments[0] = v1;
arguments[1] = v2;
arguments[2] = v3;
arguments[3] = v4;
memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
cur_skey[0].sk_argument = v1;
cur_skey[1].sk_argument = v2;
cur_skey[2].sk_argument = v3;
cur_skey[3].sk_argument = v4;
relation = table_open(cache->cc_reloid, AccessShareLock);
scandesc = systable_beginscan(relation,
cache->cc_indexoid,
IndexScanOK(cache, cur_skey),
NULL,
nkeys,
cur_skey);
ct = NULL;
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
{
ct = CatalogCacheCreateEntry(cache, ntp, arguments,
hashValue, hashIndex,
false);
ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
ct->refcount++;
ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
break;
}
systable_endscan(scandesc);
table_close(relation, AccessShareLock);
if (ct == NULL)
{
if (IsBootstrapProcessingMode())
return NULL;
ct = CatalogCacheCreateEntry(cache, NULL, arguments,
hashValue, hashIndex,
true);
CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
cache->cc_relname, hashIndex);
return NULL;
}
CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
cache->cc_relname, hashIndex);
#ifdef CATCACHE_STATS
cache->cc_newloads++;
#endif
return &ct->tuple;
}
三、跟踪分析
测试脚本
[local:/data/run/pg12]:5120 pg12@testdb=# select oid,proname,pronargs,proargtypes,proallargtypes from pg_proc where proname = 'func_test';
oid | proname | pronargs | proargtypes | proallargtypes
-------+-----------+----------+--------------+------------------------
16387 | func_test | 3 | 23 1043 1043 | {23,1043,1043,23,1043}
(1 row)
create or replace function func_test(pi_v1 in int,pi_v2 varchar,pio_v3 inout varchar,po_v4 out int,po_v5 out varchar)
returns record
as
$$
declare
begin
raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;
pio_v3 := 'pio_v3 i/o';
po_v4 := 100;
po_v5 := 'po_v5 out';
end;
$$ LANGUAGE plpgsql;
启动GDB跟踪
(gdb) b pg_proc.c:367
Breakpoint 1 at 0x5be038: file pg_proc.c, line 367.
(gdb)
(gdb) c
Continuing.
Breakpoint 1, ProcedureCreate (procedureName=0x15e3ab0 "func_test", procNamespace=2200,
replace=true, returnsSet=false, returnType=2249, proowner=10, languageObjectId=13581,
languageValidator=13580,
proclass="lazy" data-src=0x15e45c8 "\ndeclare\nbegin\n raise notice 'pi_v1 := %,pi_v2 := %,pi_v3 := %',pi_v1,pi_v2,pio_v3;\n pio_v3 := 'pio_v3 i/o';\n po_v4 := 100;\n po_v5 := 'po_v5 out';\nend;\n",
probin=0x0, prokind=102 'f', security_definer=false, isLeakProof=false, isStrict=false,
volatility=118 'v', parallel=117 'u', parameterTypes=0x16ce398,
allParameterTypes=23913456, parameterModes=23913544, parameterNames=23913600,
parameterDefaults=0x0, trftypes=0, proconfig=0, prosupport=0, procost=100, prorows=0)
at pg_proc.c:367
367 tupDesc = RelationGetDescr(rel);
(gdb)
进入SearchSysCache3
(gdb) n
370 oldtup = SearchSysCache3(PROCNAMEARGSNSP,
(gdb) step
SearchSysCache3 (cacheId=42, key1=22952624, key2=23913368, key3=2200) at syscache.c:1149
1149 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
(gdb)
输入参数:cacheId=42, key1=22952624, key2=23913368, key3=2200
其中key1->procedureName,key2->parameterTypes,key3->procNamespace
输入参数类型有3个,分别是23/1043/1043(inout参数)
(gdb) p (char *)22952624
$3 = 0x15e3ab0 "func_test"
(gdb) p ((oidvector *)23913368)[0]
$4 = {vl_len_ = 144, ndim = 1, dataoffset = 0, elemtype = 26, dim1 = 3, lbound1 = 0,
values = 0x16ce3b0}
(gdb) p ((oidvector *)23913368)[0]->values
$7 = 0x16ce3b0
(gdb) p *((oidvector *)23913368)[0]->values
$8 = 23
(gdb) p ((oidvector *)23913368)[0]->values[0]
$9 = 23
(gdb) p ((oidvector *)23913368)[0]->values[1]
$10 = 1043
(gdb) p ((oidvector *)23913368)[0]->values[2]
$11 = 1043
(gdb)
进入SearchCatCache3
(gdb) n
1151 Assert(SysCache[cacheId]->cc_nkeys == 3);
(gdb) n
1153 return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
(gdb) step
SearchCatCache3 (cache=0x1639c80, v1=22952624, v2=23913368, v3=2200) at catcache.c:1183
1183 return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
(gdb)
进入SearchCatCacheInternal
(gdb) step
SearchCatCacheInternal (cache=0x1639c80, nkeys=3, v1=22952624, v2=23913368, v3=2200, v4=0)
at catcache.c:1213
1213 Assert(IsTransactionState());
(gdb)
cache信息
(gdb) n
1215 Assert(cache->cc_nkeys == nkeys);
(gdb) p *cache
$13 = {id = 42, cc_nbuckets = 128, cc_tupdesc = 0x7f8159216ef8, cc_bucket = 0x163a160,
cc_hashfunc = {0xa48129 <namehashfast>, 0xa48257 <oidvectorhashfast>,
0xa481b0 <int4hashfast>, 0x0}, cc_fastequal = {0xa480ea <nameeqfast>,
0xa48222 <oidvectoreqfast>, 0xa48193 <int4eqfast>, 0x0}, cc_keyno = {2, 20, 3, 0},
cc_lists = {head = {prev = 0x7f81591cad60, next = 0x7f81591aab18}}, cc_ntup = 29,
cc_nkeys = 3, cc_relname = 0x7f8159217f10 "pg_proc", cc_reloid = 1255, cc_indexoid = 2691,
cc_relisshared = false, cc_next = {next = 0x1639698}, cc_skey = {{sk_flags = 0,
sk_attno = 2, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {
fn_addr = 0x9a1cf3 <nameeq>, fn_oid = 62, fn_nargs = 2, fn_strict = true,
fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0,
fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0, sk_attno = 20, sk_strategy = 3,
sk_subtype = 0, sk_collation = 950, sk_func = {fn_addr = 0x9be7e8 <oidvectoreq>,
fn_oid = 679, fn_nargs = 2, fn_strict = true, fn_retset = false, fn_stats = 2 '\002',
fn_extra = 0x0, fn_mcxt = 0x16280d0, fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0,
sk_attno = 3, sk_strategy = 3, sk_subtype = 0, sk_collation = 950, sk_func = {
fn_addr = 0x9be650 <oideq>, fn_oid = 184, fn_nargs = 2, fn_strict = true,
fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0,
fn_expr = 0x0}, sk_argument = 0}, {sk_flags = 0, sk_attno = 0, sk_strategy = 0,
sk_subtype = 0, sk_collation = 0, sk_func = {fn_addr = 0x0, fn_oid = 0, fn_nargs = 0,
---Type <return> to continue, or q <return> to quit---
fn_strict = false, fn_retset = false, fn_stats = 0 '\000', fn_extra = 0x0,
fn_mcxt = 0x0, fn_expr = 0x0}, sk_argument = 0}}}
(gdb)
pg_proc tuple描述符
(gdb) p *cache->cc_tupdesc
$14 = {natts = 29, tdtypeid = 81, tdtypmod = -1, tdrefcount = -1, constr = 0x7f8159216b90,
attrs = 0x7f8159216f10}
(gdb) p *cache->cc_tupdesc->constr
$15 = {defval = 0x0, check = 0x0, missing = 0x0, num_defval = 0, num_check = 0,
has_not_null = true, has_generated_stored = false}
(gdb) p *cache->cc_tupdesc->attrs
$16 = {attrelid = 1255, attname = {data = "oid", '\000' <repeats 60 times>}, atttypid = 26,
attstattarget = -1, attlen = 4, attnum = 1, attndims = 0, attcacheoff = 0, atttypmod = -1,
attbyval = true, attstorage = 112 'p', attalign = 105 'i', attnotnull = true,
atthasdef = false, atthasmissing = false, attidentity = 0 '\000', attgenerated = 0 '\000',
attisdropped = false, attislocal = true, attinhcount = 0, attcollation = 0}
(gdb) p cache->cc_tupdesc->attrs[1]
$17 = {attrelid = 1255, attname = {data = "proname", '\000' <repeats 56 times>},
atttypid = 19, attstattarget = -1, attlen = 64, attnum = 2, attndims = 0, attcacheoff = 4,
atttypmod = -1, attbyval = false, attstorage = 112 'p', attalign = 99 'c',
attnotnull = true, atthasdef = false, atthasmissing = false, attidentity = 0 '\000',
attgenerated = 0 '\000', attisdropped = false, attislocal = true, attinhcount = 0,
attcollation = 950}
(gdb)
获取hash桶
(gdb) n
1220 if (unlikely(cache->cc_tupdesc == NULL))
(gdb)
1228 arguments[0] = v1;
(gdb)
1229 arguments[1] = v2;
(gdb)
1230 arguments[2] = v3;
(gdb)
1231 arguments[3] = v4;
(gdb)
1236 hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
(gdb)
1237 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
(gdb)
1245 bucket = &cache->cc_bucket[hashIndex];
(gdb) p hashValue
$18 = 3879045281
(gdb) p hashIndex
$19 = 33
(gdb)
(gdb) n
1246 dlist_foreach(iter, bucket)
(gdb) p *bucket
$20 = {head = {prev = 0x7f815919d688, next = 0x7f815919d688}}
(gdb)
沿着hash桶中的链表查找
(gdb) n
1248 ct = dlist_container(CatCTup, cache_elem, iter.cur);
(gdb)
1250 if (ct->dead)
(gdb)
1253 if (ct->hash_value != hashValue)
(gdb)
1254 continue;
(gdb)
1246 dlist_foreach(iter, bucket)
(gdb)
1299 return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
(gdb) step
SearchCatCacheMiss (cache=0x1639c80, nkeys=3, hashValue=3879045281, hashIndex=33,
v1=22952624, v2=23913368, v3=2200, v4=0) at catcache.c:1327
1327 arguments[0] = v1;
(gdb)
如没有找到,则调用SearchCatCacheMiss,构建扫描键
(gdb) n
1328 arguments[1] = v2;
(gdb)
1329 arguments[2] = v3;
(gdb)
1330 arguments[3] = v4;
(gdb)
1336 memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
(gdb)
1337 cur_skey[0].sk_argument = v1;
(gdb)
1338 cur_skey[1].sk_argument = v2;
(gdb)
1339 cur_skey[2].sk_argument = v3;
(gdb)
1340 cur_skey[3].sk_argument = v4;
(gdb)
1357 relation = table_open(cache->cc_reloid, AccessShareLock);
(gdb) p *cur_skey
$21 = {sk_flags = 0, sk_attno = 2, sk_strategy = 3, sk_subtype = 0, sk_collation = 950,
sk_func = {fn_addr = 0x9a1cf3 <nameeq>, fn_oid = 62, fn_nargs = 2, fn_strict = true,
fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0,
fn_expr = 0x0}, sk_argument = 22952624}
(gdb) p cur_skey[1]
$22 = {sk_flags = 0, sk_attno = 20, sk_strategy = 3, sk_subtype = 0, sk_collation = 950,
sk_func = {fn_addr = 0x9be7e8 <oidvectoreq>, fn_oid = 679, fn_nargs = 2, fn_strict = true,
fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0,
fn_expr = 0x0}, sk_argument = 23913368}
(gdb) p cur_skey[2]
$23 = {sk_flags = 0, sk_attno = 3, sk_strategy = 3, sk_subtype = 0, sk_collation = 950,
sk_func = {fn_addr = 0x9be650 <oideq>, fn_oid = 184, fn_nargs = 2, fn_strict = true,
fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x16280d0,
fn_expr = 0x0}, sk_argument = 2200}
(gdb) p cur_skey[4]
$24 = {sk_flags = 0, sk_attno = 0, sk_strategy = 0, sk_subtype = 0,
sk_collation = 2691234902, sk_func = {fn_addr = 0x1639c98, fn_oid = 16681376,
fn_nargs = -30559, fn_strict = 53, fn_retset = 231, fn_stats = 96 '`',
fn_extra = 0xa4a825 <SearchCatCacheInternal+572>, fn_mcxt = 0x898, fn_expr = 0x0},
sk_argument = 0}
(gdb)
开始扫描
(gdb) n
1361 IndexScanOK(cache, cur_skey),
(gdb)
1359 scandesc = systable_beginscan(relation,
(gdb)
1366 ct = NULL;
(gdb)
1368 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
(gdb)
1370 ct = CatalogCacheCreateEntry(cache, ntp, arguments,
(gdb)
1374 ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
(gdb)
1375 ct->refcount++;
(gdb)
1376 ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
(gdb)
1377 break;
(gdb)
1380 systable_endscan(scandesc);
(gdb)
1382 table_close(relation, AccessShareLock);
(gdb)
1394 if (ct == NULL)
(gdb)
成功,返回tuple
1425 return &ct->tuple;
(gdb)
(gdb) p *ct
$25 = {ct_magic = 1462113538, hash_value = 3879045281, keys = {140193522567236,
140193522567344, 2200, 0}, cache_elem = {prev = 0x163a370, next = 0x7f815919d688},
refcount = 1, dead = false, negative = false, tuple = {t_len = 488, t_self = {ip_blkid = {
bi_hi = 0, bi_lo = 42}, ip_posid = 20}, t_tableOid = 1255, t_data = 0x7f81591cc820},
c_list = 0x0, my_cache = 0x1639c80}
(gdb) p ct->tuple
$26 = {t_len = 488, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 42}, ip_posid = 20},
t_tableOid = 1255, t_data = 0x7f81591cc820}
(gdb)
到此,相信大家对“分析PostgreSQL创建函数的过程”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341