目录
显示
本篇内容主要讲解“分析PostgreSQL创建函数的过程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“分析PostgreSQL创建函数的过程”吧!
一、数据结构
Form_pg_language
plpgsql语言定义结构体
/* ---------------- * pg_language definition. cpp turns this into * typedef struct FormData_pg_language * ---------------- */ CATALOG(pg_language,2612,LanguageRelationId) { Oid oid; /* oid */ /* Language name */ NameData lanname; /* Language's owner */ Oid lanowner BKI_DEFAULT(PGUID); /* Is a procedural language */ bool lanispl BKI_DEFAULT(f); /* PL is trusted */ bool lanpltrusted BKI_DEFAULT(f); /* Call handler, if it's a PL */ Oid lanplcallfoid BKI_DEFAULT(0) BKI_LOOKUP(pg_proc); /* Optional anonymous-block handler function */ Oid laninline BKI_DEFAULT(0) BKI_LOOKUP(pg_proc); /* Optional validation function */ Oid lanvalidator BKI_DEFAULT(0) BKI_LOOKUP(pg_proc); #ifdef CATALOG_VARLEN /* variable-length fields start here */ /* Access privileges */ aclitem lanacl[1] BKI_DEFAULT(_null_); #endif } FormData_pg_language; /* ---------------- * Form_pg_language corresponds to a pointer to a tuple with * the format of pg_language relation. * ---------------- */ typedef FormData_pg_language *Form_pg_language;
ArrayType
/* * Arrays are varlena objects, so must meet the varlena convention that * the first int32 of the object contains the total object size in bytes. * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though! * Arrays是可变对象集,必须符合varlena约定,即对象的第一个int32包含对象的总大小(以字节为单位)。 * 但是,一定要确保使用VARSIZE和SET_VARSIZE函数范围该结构体 * * CAUTION: if you change the header for ordinary arrays you will also * need to change the headers for oidvector and int2vector! */ typedef struct { //可变的header int32 vl_len_; /* varlena header (do not touch directly!) */ //维度 int ndim; /* # of dimensions */ //指向数据的偏移量,如为0则表示没有位图 int32 dataoffset; /* offset to data, or 0 if no bitmap */ //元素类型的OID Oid elemtype; /* element type OID */ } ArrayType;
DefElem
typedef struct DefElem { NodeTag type; char *defnamespace; /* NULL if unqualified name */ char *defname; Node *arg; /* a (Value *) or a (TypeName *) */ DefElemAction defaction; /* unspecified action, or SET/ADD/DROP */ int location; /* token location, or -1 if unknown */ } DefElem;
FunctionParameter
typedef enum FunctionParameterMode { /* the assigned enum values appear in pg_proc, don't change 'em! */ FUNC_PARAM_IN = 'i', /* input only */ FUNC_PARAM_OUT = 'o', /* output only */ FUNC_PARAM_INOUT = 'b', /* both */ FUNC_PARAM_VARIADIC = 'v', /* variadic (always input) */ FUNC_PARAM_TABLE = 't' /* table function output column */ } FunctionParameterMode; typedef struct FunctionParameter { NodeTag type; char *name; /* parameter name, or NULL if not given */ TypeName *argType; /* TypeName for parameter type */ FunctionParameterMode mode; /* IN/OUT/etc */ Node *defexpr; /* raw default expr, or NULL if not given */ } FunctionParameter;
CatCache
/* function computing a datum's hash */ typedef uint32 (*CCHashFN) (Datum datum); /* function computing equality of two datums */ typedef bool (*CCFastEqualFN) (Datum a, Datum b); typedef struct catcache { //cache ID int id; /* cache identifier --- see syscache.h */ //cache的hash槽 int cc_nbuckets; /* # of hash buckets in this cache */ //元组描述符 TupleDesc cc_tupdesc; /* tuple descriptor (copied from reldesc) */ //hash桶 dlist_head *cc_bucket; /* hash buckets */ //每个key的hash函数 CCHashFN cc_hashfunc[CATCACHE_MAXKEYS]; /* hash function for each key */ //每个key的快速等值函数 CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS]; /* fast equal function for * each key */ //每个key的属性编号 int cc_keyno[CATCACHE_MAXKEYS]; /* AttrNumber of each key */ //CatCList结构体链表 dlist_head cc_lists; /* list of CatCList structs */ //cache中元组数 int cc_ntup; /* # of tuples currently in this cache */ //keys数 int cc_nkeys; /* # of keys (1..CATCACHE_MAXKEYS) */ //cache元组相关的relation const char *cc_relname; /* name of relation the tuples come from */ //relation OID Oid cc_reloid; /* OID of relation the tuples come from */ //匹配缓存keys的索引OID Oid cc_indexoid; /* OID of index matching cache keys */ //是否可跨库共享? bool cc_relisshared; /* is relation shared across databases? */ //链表链接 slist_node cc_next; /* list link */ //用于heap扫描的预计算key信息 ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap * scans */ /* * Keep these at the end, so that compiling catcache.c with CATCACHE_STATS * doesn't break ABI for other modules * 这些项放在最后面,以便使用CATCACHE_STATS选项编译catcache.c时不需要终止其他模块的ABI */ #ifdef CATCACHE_STATS //检索次数 long cc_searches; /* total # searches against this cache */ //匹配次数 long cc_hits; /* # of matches against existing entry */ //未命中次数 long cc_neg_hits; /* # of matches against negative entry */ //未命中成功加载次数 long cc_newloads; /* # of successful loads of new entry */ /* * cc_searches - (cc_hits + cc_neg_hits + cc_newloads) is number of failed * searches, each of which will result in loading a negative entry\\\\ * cc_searches - (cc_hits + cc_neg_hits + cc_newloads)是cache检索失败次数 */ //验证失效次数 long cc_invals; /* # of entries invalidated from cache */ //链表检索次数 long cc_lsearches; /* total # list-searches */ // long cc_lhits; /* # of matches against existing lists */ #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); } /* * Work-horse for SearchCatCache/SearchCatCacheN. * 通用函数:SearchCatCache/SearchCatCacheN调用 */ 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; /* Make sure we're in an xact, even if this ends up being a cache hit */ //确保处于事务中 Assert(IsTransactionState()); Assert(cache->cc_nkeys == nkeys); /* * one-time startup overhead for each cache */ if (unlikely(cache->cc_tupdesc == NULL)) CatalogCacheInitializeCache(cache); #ifdef CATCACHE_STATS cache->cc_searches++; #endif /* Initialize local parameter array */ //初始化本地参数数组 arguments[0] = v1; arguments[1] = v2; arguments[2] = v3; arguments[3] = v4; /* * find the hash bucket in which to look for the tuple * 检索hash桶 */ hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); /* * scan the hash bucket until we find a match or exhaust our tuples * 扫描hash桶 * * Note: it's okay to use dlist_foreach here, even though we modify the * dlist within the loop, because we don't continue the loop afterwards. * 就算在循环过程中更新了dlist也可以用dlist_foreach,因为不再往后循环了 */ bucket = &cache->cc_bucket[hashIndex]; dlist_foreach(iter, bucket) { ct = dlist_container(CatCTup, cache_elem, iter.cur); if (ct->dead)//忽略已废弃的条目 continue; /* ignore dead entries */ if (ct->hash_value != hashValue)//跳过hash不同的项 continue; /* quickly skip entry if wrong hash val */ //不同的元组,跳过 if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments)) continue; /* * We found a match in the cache. Move it to the front of the list * for its hashbucket, in order to speed subsequent searches. (The * most frequently accessed elements in any hashbucket will tend to be * near the front of the hashbucket's list.) * 命中,放在链表的头部 */ dlist_move_head(bucket, &ct->cache_elem); /* * If it's a positive entry, bump its refcount and return it. If it's * negative, we can report failure to the caller. * 正向项,增加refcount并返回 */ 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); } /* * Search the actual catalogs, rather than the cache. * * This is kept separate from SearchCatCacheInternal() to keep the fast-path * as small as possible. To avoid that effort being undone by a helpful * compiler, try to explicitly forbid inlining. */ 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]; /* Initialize local parameter array */ arguments[0] = v1; arguments[1] = v2; arguments[2] = v3; arguments[3] = v4; /* * Ok, need to make a lookup in the relation, copy the scankey and fill * out any per-call fields. */ 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; /* * Tuple was not found in cache, so we have to try to retrieve it directly * from the relation. If found, we will add it to the cache; if not * found, we will add a negative cache entry instead. * * NOTE: it is possible for recursive cache lookups to occur while reading * the relation --- for example, due to shared-cache-inval messages being * processed during table_open(). This is OK. It's even possible for one * of those lookups to find and enter the very same tuple we are trying to * fetch here. If that happens, we will enter a second copy of the tuple * into the cache. The first copy will never be referenced again, and * will eventually age out of the cache, so there's no functional problem. * This case is rare enough that it's not worth expending extra cycles to * detect. */ 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); /* immediately set the refcount to 1 */ ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); ct->refcount++; ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); break; /* assume only one match */ } systable_endscan(scandesc); table_close(relation, AccessShareLock); /* * If tuple was not found, we need to build a negative cache entry * containing a fake tuple. The fake tuple has the correct key columns, * but nulls everywhere else. * * In bootstrap mode, we don't build negative entries, because the cache * invalidation mechanism isn't alive and can't clear them if the tuple * gets created later. (Bootstrap doesn't do UPDATEs, so it doesn't need * cache inval for that.) */ 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); /* * We are not returning the negative entry to the caller, so leave its * refcount zero. */ 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, prosrc=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; /* quickly skip entry if wrong hash val */ (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; /* assume only one match */ (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创建函数的过程”有了更深的了解,不妨来实际操作一番吧!这里是云搜网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!